1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-22 20:52:27 +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"] "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] #[test]
fn trybuild() { fn trybuild() {
let t = TestCases::new(); let t = TestCases::new();
t.pass("tests/pass/*.rs");
t.compile_fail("tests/fail/*.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 proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use syn::{LitStr, Type}; use syn::LitStr;
impl ParseData { impl ParseData {
pub(super) fn gen_schema(self) -> syn::Result<TokenStream> { pub(super) fn gen_schema(&self) -> TokenStream {
match self { match self {
Self::Struct(fields) => gen_struct(fields), Self::Struct(fields) => gen_struct(fields),
Self::Enum(variants) => gen_enum(variants),
Self::Unit => gen_unit(), Self::Unit => gen_unit(),
_ => unimplemented!() _ => 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_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); let openapi = path!(::openapi_type::openapi);
Ok(quote! { quote! {
{ {
let mut properties = <::openapi_type::indexmap::IndexMap< let mut properties = <::openapi_type::indexmap::IndexMap<
::std::string::String, ::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; 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( ::openapi_type::private::add_dependencies(
&mut dependencies, &mut dependencies,
&mut field_schema.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); 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::SchemaKind::Type(
#openapi::Type::Object( #openapi::Type::Object(
#openapi::ObjectType { #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 // run the codegen
let schema_code = parsed.gen_schema()?; let schema_code = parsed.gen_schema();
// put the code together // put the code together
Ok(quote! { Ok(quote! {

View file

@ -1,35 +1,93 @@
use crate::util::ToLitStr; 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)] #[allow(dead_code)]
pub(super) enum ParseData { pub(super) enum ParseData {
Struct(Vec<(LitStr, Type)>), Struct(Vec<(LitStr, ParseDataType)>),
Enum(Vec<LitStr>), Enum(Vec<LitStr>),
Alternatives(Vec<ParseData>), Alternatives(Vec<ParseData>),
Unit 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> { pub(super) fn parse_struct(strukt: &DataStruct) -> syn::Result<ParseData> {
match &strukt.fields { match &strukt.fields {
Fields::Named(named_fields) => { Fields::Named(named_fields) => parse_named_fields(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::Unnamed(_) => unimplemented!(), Fields::Unnamed(_) => unimplemented!(),
Fields::Unit => Ok(ParseData::Unit) Fields::Unit => Ok(ParseData::Unit)
} }
} }
pub(super) fn parse_enum(_inum: &DataEnum) -> syn::Result<ParseData> { pub(super) fn parse_enum(inum: &DataEnum) -> syn::Result<ParseData> {
unimplemented!() 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> { pub(super) fn parse_union(union: &DataUnion) -> syn::Result<ParseData> {