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:
parent
667009bd22
commit
43d3a1cd89
8 changed files with 159 additions and 35 deletions
|
@ -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"]
|
||||||
|
});
|
||||||
|
|
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]
|
#[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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue