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:
parent
667009bd22
commit
43d3a1cd89
8 changed files with 159 additions and 35 deletions
|
@ -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"]
|
||||
});
|
||||
|
|
6
openapi_type/tests/fail/enum_with_no_variants.rs
Normal file
6
openapi_type/tests/fail/enum_with_no_variants.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use openapi_type::OpenapiType;
|
||||
|
||||
#[derive(OpenapiType)]
|
||||
enum Foo {}
|
||||
|
||||
fn main() {}
|
5
openapi_type/tests/fail/enum_with_no_variants.stderr
Normal file
5
openapi_type/tests/fail/enum_with_no_variants.stderr
Normal 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 {}
|
||||
| ^^
|
|
@ -1,6 +0,0 @@
|
|||
use openapi_type_derive::OpenapiType;
|
||||
|
||||
#[derive(OpenapiType)]
|
||||
struct Foo;
|
||||
|
||||
fn main() {}
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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> {
|
|||
}
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Add table
Reference in a new issue