1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-22 12:42:28 +00:00

start implementing enums

This commit is contained in:
Dominic 2021-03-08 17:20:41 +01:00
parent 667009bd22
commit 43d3a1cd89
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
8 changed files with 159 additions and 35 deletions

View file

@ -42,3 +42,38 @@ test_type!(SimpleStruct = {
},
"required": ["foo", "bar"]
});
#[derive(OpenapiType)]
enum EnumWithoutFields {
Success,
Error
}
test_type!(EnumWithoutFields = {
"type": "string",
"title": "EnumWithoutFields",
"enum": [
"Success",
"Error"
]
});
#[derive(OpenapiType)]
enum EnumWithOneField {
Success { value: isize }
}
test_type!(EnumWithOneField = {
"type": "object",
"title": "EnumWithOneField",
"properties": {
"Success": {
"type": "object",
"properties": {
"value": {
"type": "integer"
}
},
"required": ["value"]
}
},
"required": ["Success"]
});

View file

@ -0,0 +1,6 @@
use openapi_type::OpenapiType;
#[derive(OpenapiType)]
enum Foo {}
fn main() {}

View file

@ -0,0 +1,5 @@
error: #[derive(OpenapiType)] does not support enums with no variants
--> $DIR/enum_with_no_variants.rs:4:10
|
4 | enum Foo {}
| ^^

View file

@ -1,6 +0,0 @@
use openapi_type_derive::OpenapiType;
#[derive(OpenapiType)]
struct Foo;
fn main() {}

View file

@ -3,6 +3,5 @@ use trybuild::TestCases;
#[test]
fn trybuild() {
let t = TestCases::new();
t.pass("tests/pass/*.rs");
t.compile_fail("tests/fail/*.rs");
}

View file

@ -1,24 +1,33 @@
use crate::parser::ParseData;
use crate::parser::{ParseData, ParseDataType};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{LitStr, Type};
use syn::LitStr;
impl ParseData {
pub(super) fn gen_schema(self) -> syn::Result<TokenStream> {
pub(super) fn gen_schema(&self) -> TokenStream {
match self {
Self::Struct(fields) => gen_struct(fields),
Self::Enum(variants) => gen_enum(variants),
Self::Unit => gen_unit(),
_ => unimplemented!()
}
}
}
fn gen_struct(fields: Vec<(LitStr, Type)>) -> syn::Result<TokenStream> {
fn gen_struct(fields: &[(LitStr, ParseDataType)]) -> TokenStream {
let field_name = fields.iter().map(|(name, _)| name);
let field_ty = fields.iter().map(|(_, ty)| ty);
let field_schema = fields.iter().map(|(_, ty)| match ty {
ParseDataType::Type(ty) => {
quote!(<#ty as ::openapi_type::OpenapiType>::schema())
},
ParseDataType::Inline(data) => {
let code = data.gen_schema();
quote!(::openapi_type::OpenapiSchema::new(#code))
}
});
let openapi = path!(::openapi_type::openapi);
Ok(quote! {
quote! {
{
let mut properties = <::openapi_type::indexmap::IndexMap<
::std::string::String,
@ -28,7 +37,7 @@ fn gen_struct(fields: Vec<(LitStr, Type)>) -> syn::Result<TokenStream> {
#({
const FIELD_NAME: &::core::primitive::str = #field_name;
let mut field_schema = <#field_ty as ::openapi_type::OpenapiType>::schema();
let mut field_schema = #field_schema;
::openapi_type::private::add_dependencies(
&mut dependencies,
&mut field_schema.dependencies
@ -78,12 +87,30 @@ fn gen_struct(fields: Vec<(LitStr, Type)>) -> syn::Result<TokenStream> {
)
)
}
})
}
}
fn gen_unit() -> syn::Result<TokenStream> {
fn gen_enum(variants: &[LitStr]) -> TokenStream {
let openapi = path!(::openapi_type::openapi);
Ok(quote! {
quote! {
{
let mut enumeration = <::std::vec::Vec<::std::string::String>>::new();
#(enumeration.push(::std::string::String::from(#variants));)*
#openapi::SchemaKind::Type(
#openapi::Type::String(
#openapi::StringType {
enumeration,
.. ::std::default::Default::default()
}
)
)
}
}
}
fn gen_unit() -> TokenStream {
let openapi = path!(::openapi_type::openapi);
quote! {
#openapi::SchemaKind::Type(
#openapi::Type::Object(
#openapi::ObjectType {
@ -94,5 +121,5 @@ fn gen_unit() -> syn::Result<TokenStream> {
}
)
)
})
}
}

View file

@ -51,7 +51,7 @@ fn expand_openapi_type(mut input: DeriveInput) -> syn::Result<TokenStream2> {
};
// run the codegen
let schema_code = parsed.gen_schema()?;
let schema_code = parsed.gen_schema();
// put the code together
Ok(quote! {

View file

@ -1,35 +1,93 @@
use crate::util::ToLitStr;
use syn::{spanned::Spanned as _, DataEnum, DataStruct, DataUnion, Fields, LitStr, Type};
use syn::{spanned::Spanned as _, DataEnum, DataStruct, DataUnion, Fields, FieldsNamed, LitStr, Type};
pub(super) enum ParseDataType {
Type(Type),
Inline(ParseData)
}
#[allow(dead_code)]
pub(super) enum ParseData {
Struct(Vec<(LitStr, Type)>),
Struct(Vec<(LitStr, ParseDataType)>),
Enum(Vec<LitStr>),
Alternatives(Vec<ParseData>),
Unit
}
fn parse_named_fields(named_fields: &FieldsNamed) -> syn::Result<ParseData> {
let mut fields: Vec<(LitStr, ParseDataType)> = Vec::new();
for f in &named_fields.named {
let ident = f
.ident
.as_ref()
.ok_or_else(|| syn::Error::new(f.span(), "#[derive(OpenapiType)] does not support fields without an ident"))?;
let name = ident.to_lit_str();
let ty = f.ty.to_owned();
fields.push((name, ParseDataType::Type(ty)));
}
Ok(ParseData::Struct(fields))
}
pub(super) fn parse_struct(strukt: &DataStruct) -> syn::Result<ParseData> {
match &strukt.fields {
Fields::Named(named_fields) => {
let mut fields: Vec<(LitStr, Type)> = Vec::new();
for f in &named_fields.named {
let ident = f.ident.as_ref().ok_or_else(|| {
syn::Error::new(f.span(), "#[derive(OpenapiType)] does not support fields without an ident")
})?;
let name = ident.to_lit_str();
let ty = f.ty.to_owned();
fields.push((name, ty));
}
Ok(ParseData::Struct(fields))
},
Fields::Named(named_fields) => parse_named_fields(named_fields),
Fields::Unnamed(_) => unimplemented!(),
Fields::Unit => Ok(ParseData::Unit)
}
}
pub(super) fn parse_enum(_inum: &DataEnum) -> syn::Result<ParseData> {
unimplemented!()
pub(super) fn parse_enum(inum: &DataEnum) -> syn::Result<ParseData> {
let mut strings: Vec<LitStr> = Vec::new();
let mut types: Vec<(LitStr, ParseData)> = Vec::new();
for v in &inum.variants {
let name = v.ident.to_lit_str();
match &v.fields {
Fields::Named(named_fields) => {
types.push((name, parse_named_fields(named_fields)?));
},
Fields::Unnamed(_unnamed_fields) => unimplemented!(),
Fields::Unit => strings.push(name)
}
}
let data_strings = if strings.is_empty() {
None
} else {
Some(ParseData::Enum(strings))
};
let data_types = if types.is_empty() {
None
} else {
Some(ParseData::Alternatives(
types
.into_iter()
.map(|(name, data)| ParseData::Struct(vec![(name, ParseDataType::Inline(data))]))
.collect()
))
};
match (data_strings, data_types) {
// only variants without fields
(Some(data), None) => Ok(data),
// only one variant with fields
(None, Some(ParseData::Alternatives(mut alt))) if alt.len() == 1 => Ok(alt.remove(0)),
// only variants with fields
(None, Some(data)) => Ok(data),
// variants with and without fields
(Some(data), Some(ParseData::Alternatives(mut alt))) => {
alt.push(data);
Ok(ParseData::Alternatives(alt))
},
// no variants
(None, None) => Err(syn::Error::new(
inum.brace_token.span,
"#[derive(OpenapiType)] does not support enums with no variants"
)),
// data_types always produces Alternatives
_ => unreachable!()
}
}
pub(super) fn parse_union(union: &DataUnion) -> syn::Result<ParseData> {