openapi_type_derive/
lib.rs1#![allow(clippy::into_iter_on_ref)]
2#![warn(missing_debug_implementations, rust_2018_idioms)]
3#![deny(rustdoc::broken_intra_doc_links)]
4#![forbid(unsafe_code)]
5
6use proc_macro::TokenStream;
9use proc_macro2::TokenStream as TokenStream2;
10use quote::quote;
11use syn::{parse_macro_input, Data, DeriveInput, Meta, TraitBound, TraitBoundModifier, TypeParamBound};
12use syn_path::path;
13
14mod attrs;
15mod codegen;
16mod parser;
17mod util;
18
19use attrs::*;
20use parser::*;
21
22#[proc_macro_derive(OpenapiType, attributes(openapi))]
26pub fn derive_openapi_type(input: TokenStream) -> TokenStream {
27 let input = parse_macro_input!(input);
28 expand_openapi_type(input).unwrap_or_else(|err| err.to_compile_error()).into()
29}
30
31fn filter_parse_attrs(
32 attrs: &mut ContainerAttributes,
33 input: &DeriveInput,
34 filter: &str,
35 error_on_unknown: bool
36) -> syn::Result<()> {
37 for attr in &input.attrs {
38 match &attr.meta {
39 Meta::List(meta) if meta.path.is_ident(filter) => {
40 attrs.parse_from(meta.tokens.clone(), error_on_unknown)?;
41 },
42 _ => {}
43 }
44 }
45 Ok(())
46}
47
48fn expand_openapi_type(mut input: DeriveInput) -> syn::Result<TokenStream2> {
49 let ident = &input.ident;
50
51 let mut attrs = ContainerAttributes::default();
53 filter_parse_attrs(&mut attrs, &input, "serde", false)?;
54 filter_parse_attrs(&mut attrs, &input, "openapi", true)?;
55
56 for attr in &input.attrs {
58 if attr.path().is_ident("doc") {
59 if let Some(lit) = parse_doc_attr(attr)? {
60 attrs.doc.push(lit.value());
61 }
62 }
63 }
64
65 let (impl_generics, ty_generics, where_clause) = {
67 let generics = &mut input.generics;
68 generics.type_params_mut().for_each(|param| {
69 param.colon_token.get_or_insert_with(Default::default);
70 param.bounds.push(TypeParamBound::Trait(TraitBound {
71 paren_token: None,
72 modifier: TraitBoundModifier::None,
73 lifetimes: None,
74 path: path!(::openapi_type::OpenapiType)
75 }));
76 });
77 generics.split_for_impl()
78 };
79
80 let parsed = match &input.data {
82 Data::Struct(strukt) => parse_struct(ident, strukt, &attrs)?,
83 Data::Enum(inum) => parse_enum(ident, inum, &attrs)?,
84 Data::Union(union) => parse_union(union)?
85 };
86
87 let visit_impl = parsed.gen_visit_impl();
89
90 Ok(quote! {
92 #[allow(unused_mut)]
93 impl #impl_generics ::openapi_type::OpenapiType for #ident #ty_generics #where_clause {
94 fn visit_type<__openapi_type_V>(visitor: &mut __openapi_type_V)
95 where
96 __openapi_type_V: ::openapi_type::Visitor
97 {
98 #visit_impl
99 }
100 }
101 })
102}