trans_schema_derive/
lib.rs1#![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| ¶m.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 generic_params: Vec<_> = ast
131 .generics
132 .type_params()
133 .map(|param| ¶m.ident)
134 .collect();
135 let generic_params = &generic_params;
136 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}