1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#![warn(missing_debug_implementations, rust_2018_idioms)]
#![deny(rustdoc::broken_intra_doc_links)]
#![forbid(unsafe_code)]
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, LitStr, TraitBound, TraitBoundModifier, TypeParamBound};
#[macro_use]
mod util;
mod attrs;
use attrs::*;
mod codegen;
use codegen::*;
mod parser;
use parser::*;
#[proc_macro_derive(OpenapiType, attributes(openapi))]
pub fn derive_openapi_type(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input);
expand_openapi_type(input).unwrap_or_else(|err| err.to_compile_error()).into()
}
fn expand_openapi_type(mut input: DeriveInput) -> syn::Result<TokenStream2> {
let mut attrs = ContainerAttributes::default();
for attr in &input.attrs {
if attr.path.is_ident("serde") {
attrs.parse_from(attr, false)?;
}
}
for attr in &input.attrs {
if attr.path.is_ident("openapi") {
attrs.parse_from(attr, true)?;
}
}
let mut doc: Vec<String> = Vec::new();
for attr in &input.attrs {
if attr.path.is_ident("doc") {
if let Some(lit) = parse_doc_attr(attr)? {
doc.push(lit.value());
}
}
}
let doc = gen_doc_option(&doc);
let ident = &input.ident;
let name = ident.to_string();
let mut name = LitStr::new(&name, ident.span());
if let Some(rename) = &attrs.rename {
name = rename.clone();
}
let (impl_generics, ty_generics, where_clause) = {
let generics = &mut input.generics;
generics.type_params_mut().for_each(|param| {
param.colon_token.get_or_insert_with(Default::default);
param.bounds.push(TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: path!(::openapi_type::OpenapiType)
}));
});
generics.split_for_impl()
};
let parsed = match &input.data {
Data::Struct(strukt) => parse_struct(ident, strukt, &attrs)?,
Data::Enum(inum) => parse_enum(ident, inum, &attrs)?,
Data::Union(union) => parse_union(union)?
};
let schema_code = parsed.gen_schema();
Ok(quote! {
#[allow(unused_mut)]
impl #impl_generics ::openapi_type::OpenapiType for #ident #ty_generics #where_clause {
fn schema() -> ::openapi_type::OpenapiSchema {
let mut dependencies = ::openapi_type::private::Dependencies::new();
let mut schema: ::openapi_type::OpenapiSchema = #schema_code;
schema.nullable = false;
schema.dependencies = dependencies;
const NAME: &::core::primitive::str = #name;
schema.name = ::std::option::Option::Some(::std::string::String::from(NAME));
const DESCRIPTION: ::core::option::Option<&'static ::core::primitive::str> = #doc;
schema.description = DESCRIPTION.map(|desc| ::std::string::String::from(desc));
schema
}
}
})
}