trans_schema_derive/
lib.rs

1#![recursion_limit = "256"]
2extern crate proc_macro;
3
4use quote::quote;
5
6use proc_macro2::TokenStream;
7
8#[proc_macro_derive(Schematic, attributes(schematic))]
9pub fn derive_schematic(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10    let input: TokenStream = input.into();
11    let result: TokenStream = {
12        let ast: syn::DeriveInput = syn::parse_str(&input.to_string()).unwrap();
13        let input_type = &ast.ident;
14        let mut base_name =
15            syn::LitStr::new(&ast.ident.to_string(), proc_macro2::Span::call_site());
16        let mut magic: Option<syn::Expr> = None;
17        for attr in &ast.attrs {
18            if let Ok(syn::Meta::List(syn::MetaList {
19                path: ref meta_path,
20                ref nested,
21                ..
22            })) = attr.parse_meta()
23            {
24                if meta_path.is_ident("schematic") {
25                    for inner in nested {
26                        if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
27                            path: ref meta_path,
28                            lit: syn::Lit::Str(ref lit),
29                            ..
30                        })) = *inner
31                        {
32                            if meta_path.is_ident("rename") {
33                                base_name = lit.clone();
34                            } else if meta_path.is_ident("magic") {
35                                magic = Some(syn::parse_str(&lit.value()).unwrap());
36                            }
37                        }
38                    }
39                }
40            }
41        }
42        let magic = match magic {
43            Some(expr) => quote! { Some(#expr) },
44            None => quote! { None },
45        };
46        match ast.data {
47            syn::Data::Struct(syn::DataStruct { ref fields, .. }) => match fields {
48                syn::Fields::Named(_) => {
49                    let field_tys: Vec<_> = fields.iter().map(|field| &field.ty).collect();
50                    let field_tys = &field_tys;
51                    let field_names: Vec<_> = fields
52                        .iter()
53                        .map(|field| field.ident.as_ref().unwrap())
54                        .collect();
55                    let field_names = &field_names;
56                    let mut generics = ast.generics.clone();
57                    let generic_params: Vec<_> = ast
58                        .generics
59                        .type_params()
60                        .map(|param| &param.ident)
61                        .collect();
62                    let generic_params = &generic_params;
63                    let extra_where_clauses = quote! {
64                        where
65                            #(#field_tys: trans_schema::Schematic + 'static,)*
66                            #(#generic_params: trans_schema::Schematic,)*
67                    };
68                    let extra_where_clauses: syn::WhereClause =
69                        syn::parse_str(&extra_where_clauses.to_string()).unwrap();
70                    generics
71                        .make_where_clause()
72                        .predicates
73                        .extend(extra_where_clauses.predicates);
74                    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
75                    let expanded = quote! {
76                        impl #impl_generics trans_schema::Schematic for #input_type #ty_generics #where_clause {
77                            fn create_schema() -> trans_schema::Schema {
78                                let mut name = #base_name.to_owned();
79                                #(
80                                    name += &trans_schema::schema::<#generic_params>().full_name().raw();
81                                )*
82                                trans_schema::Schema::Struct(trans_schema::Struct {
83                                    name: trans_schema::Name::new(name),
84                                    magic: #magic,
85                                    fields: vec![
86                                        #(trans_schema::Field {
87                                            name: trans_schema::Name::new(stringify!(#field_names).to_owned()),
88                                            schema: trans_schema::schema::<#field_tys>(),
89                                        }),*
90                                    ],
91                                })
92                            }
93                        }
94                    };
95                    expanded.into()
96                }
97                syn::Fields::Unnamed(_) => {
98                    if fields.iter().len() != 1 {
99                        panic!("Tuple structs other than newtype not supported");
100                    }
101                    let inner_ty = fields.iter().next().unwrap();
102                    let mut generics = ast.generics.clone();
103                    let extra_where_clauses = quote! {
104                        where #inner_ty: trans_schema::Schematic + 'static
105                    };
106                    let extra_where_clauses: syn::WhereClause =
107                        syn::parse_str(&extra_where_clauses.to_string()).unwrap();
108                    generics
109                        .make_where_clause()
110                        .predicates
111                        .extend(extra_where_clauses.predicates);
112                    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
113                    let expanded = quote! {
114                        impl #impl_generics trans_schema::Schematic for #input_type #ty_generics #where_clause {
115                            fn create_schema() -> trans_schema::Schema {
116                                <#inner_ty as trans_schema::Schematic>::create_schema()
117                            }
118                        }
119                    };
120                    expanded.into()
121                }
122                syn::Fields::Unit => panic!("Unit structs not supported"),
123            },
124            syn::Data::Enum(syn::DataEnum { ref variants, .. }) => {
125                let mut generics = ast.generics.clone();
126                // let all_field_tys = variants
127                //     .iter()
128                //     .map(|variant| variant.fields.iter().map(|field| &field.ty))
129                //     .flatten();
130                let generic_params: Vec<_> = ast
131                    .generics
132                    .type_params()
133                    .map(|param| &param.ident)
134                    .collect();
135                let generic_params = &generic_params;
136                // let extra_where_clauses = quote! {
137                //     where
138                //         #(#all_field_tys: trans_schema::Schematic + 'static,)*
139                //         #(#generic_params: trans_schema::Schematic,)*
140                // };
141                // let extra_where_clauses: syn::WhereClause =
142                //     syn::parse_str(&extra_where_clauses.to_string()).unwrap();
143                // generics
144                //     .make_where_clause()
145                //     .predicates
146                //     .extend(extra_where_clauses.predicates);
147                let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
148                if variants.iter().all(|variant| {
149                    if let syn::Fields::Unit = variant.fields {
150                        true
151                    } else {
152                        false
153                    }
154                }) {
155                    let variants = variants.iter();
156                    let expanded = quote! {
157                        impl #impl_generics trans_schema::Schematic for #input_type #ty_generics #where_clause {
158                            fn create_schema() -> trans_schema::Schema {
159                                let mut base_name = #base_name.to_owned();
160                                #(
161                                    base_name += &trans_schema::schema::<#generic_params>().full_name().raw();
162                                )*
163                                trans_schema::Schema::Enum {
164                                    base_name: trans_schema::Name::new(base_name),
165                                    variants: vec![#(trans_schema::Name::new(stringify!(#variants).to_owned())),*],
166                                }
167                            }
168                        }
169                    };
170                    expanded.into()
171                } else {
172                    let variants = variants.iter().map(|variant| {
173                        let variant_name = &variant.ident;
174                        let field_names = variant
175                            .fields
176                            .iter()
177                            .map(|field| field.ident.as_ref().unwrap());
178                        let field_tys = variant.fields.iter().map(|field| &field.ty);
179                        quote! {
180                            trans_schema::Struct {
181                                name: trans_schema::Name::new(stringify!(#variant_name).to_owned()),
182                                magic: None,
183                                fields: vec![
184                                    #(trans_schema::Field {
185                                        name: trans_schema::Name::new(stringify!(#field_names).to_owned()),
186                                        schema: trans_schema::schema::<#field_tys>(),
187                                    }),*
188                                ],
189                            }
190                        }
191                    });
192                    let expanded = quote! {
193                        impl #impl_generics trans_schema::Schematic for #input_type #ty_generics #where_clause {
194                            fn create_schema() -> trans_schema::Schema {
195                                let mut base_name = #base_name.to_owned();
196                                #(
197                                    base_name += &trans_schema::schema::<#generic_params>().full_name().raw();
198                                )*
199                                trans_schema::Schema::OneOf {
200                                    base_name: trans_schema::Name::new(base_name),
201                                    variants: vec![#(#variants),*],
202                                }
203                            }
204                        }
205                    };
206                    expanded.into()
207                }
208            }
209            syn::Data::Union(_) => panic!("Unions not supported"),
210        }
211    };
212    result.into()
213}