typenaming_derive/
lib.rs

1use proc_macro::{self, TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5fn add_trait_bounds(mut generics: syn::Generics) -> syn::Generics {
6    for param in &mut generics.params {
7        if let syn::GenericParam::Type(ref mut type_param) = *param {
8            type_param.bounds.push(syn::parse_quote!(TypeNameable));
9        }
10    }
11    generics
12}
13
14use darling::FromDeriveInput;
15
16#[derive(FromDeriveInput, Debug)]
17#[darling(attributes(typenameable))]
18struct TypeNameArguments {
19    #[darling(default)]
20    type_name: Option<syn::Ident>,
21    #[darling(default)]
22    crate_name: Option<syn::Ident>,
23    #[darling(default)]
24    crate_module: Option<String>,
25    #[darling(default)]
26    crate_version: Option<String>,
27    #[darling(default)]
28    rustc_version: Option<String>,
29    #[darling(default)]
30    default_to_none: bool,
31}
32
33#[proc_macro_derive(TypeNameable, attributes(typenameable))]
34pub fn derive_type_name(tokens: TokenStream) -> TokenStream {
35    let derived = parse_macro_input!(tokens);
36    let TypeNameArguments {
37        type_name,
38        crate_name,
39        crate_module,
40        crate_version,
41        rustc_version,
42        default_to_none,
43    } = TypeNameArguments::from_derive_input(&derived).unwrap();
44
45    let DeriveInput {
46        ident,
47        attrs: _,
48        vis: _,
49        generics,
50        data: _,
51    } = derived;
52    let generics = add_trait_bounds(generics);
53    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
54
55    let type_name = type_name
56        .map(|x| x.to_string())
57        .unwrap_or_else(|| ident.to_string());
58    let crate_name = if let Some(crate_name) = crate_name {
59        quote!(Some(stringify!(#crate_name).to_owned()))
60    } else if default_to_none {
61        quote!(None)
62    } else {
63        quote!(Some(env!("CARGO_PKG_NAME").to_owned()))
64    };
65    let module_path_import;
66    let crate_module = if let Some(crate_module) = crate_module {
67        module_path_import = quote! {};
68        quote!(Some(#crate_module.to_owned()))
69    } else if default_to_none {
70        module_path_import = quote! {};
71        quote!(None)
72    } else {
73        module_path_import = quote! {use ::std::module_path as std_module_path_module_path;};
74        quote!(Some(std_module_path_module_path!().to_owned()))
75    };
76    let crate_version = if let Some(crate_version) = crate_version {
77        quote!(Some(
78                <::typenaming::Version as ::std::str::FromStr>::from_str(
79                #crate_version
80            )
81            .expect(&format!("Failed to parse crate version argument '{}'", #crate_version)))
82        )
83    } else if default_to_none {
84        quote!(None)
85    } else {
86        quote!(Some(::typenaming::new_semver_version(
87            env!("CARGO_PKG_VERSION_MAJOR"),
88            env!("CARGO_PKG_VERSION_MINOR"),
89            env!("CARGO_PKG_VERSION_PATCH"),
90            env!("CARGO_PKG_VERSION_PRE")
91        )))
92    };
93    let rustc_version = if let Some(rustc_version) = rustc_version {
94        quote!(Some(
95            <::typenaming::Version as ::std::str::FromStr>::from_str(
96                #rustc_version
97            )
98            .expect(&format!("Failed to parse rustc version argument '{}'", #rustc_version))
99        ))
100    } else if default_to_none {
101        quote!(None)
102    } else {
103        quote!(Some(
104            ::typenaming::rustc_version().expect("Failed to fetch rustc version")
105        ))
106    };
107    let generics = generics
108        .type_params()
109        .map(|x| x.ident.clone())
110        .collect::<Vec<_>>();
111    let generics = quote!(#(<#generics as ::typenaming::TypeNameable>::type_info()),*);
112    let body = quote! {
113        #module_path_import
114        ::typenaming::TypeInfo::new(
115            #type_name.to_owned(),
116            #crate_name,
117            #crate_module,
118            #crate_version,
119            #rustc_version,
120            vec![
121              #generics
122            ]
123        )
124    };
125    quote! {
126        #[automatically_derived]
127        impl #impl_generics TypeNameable for #ident #ty_generics #where_clause {
128            fn type_info() -> ::typenaming::TypeInfo {
129                #body
130            }
131        }
132    }
133    .into()
134}
135
136#[derive(FromDeriveInput, Debug)]
137#[darling(attributes(typequeryable))]
138struct TypeQueryArguments {
139    #[darling(default)]
140    type_name: Option<syn::Ident>,
141    #[darling(default)]
142    crate_name: Option<syn::Ident>,
143    #[darling(default)]
144    crate_module: Option<String>,
145    #[darling(default)]
146    crate_version: Option<String>,
147    #[darling(default)]
148    rustc_version: Option<String>,
149    #[darling(default)]
150    default_to_none: bool,
151}
152
153#[proc_macro_derive(TypeQueryable, attributes(typequeryable))]
154pub fn typequeryable(tokens: TokenStream) -> TokenStream {
155    let derived = parse_macro_input!(tokens);
156    let TypeQueryArguments {
157        type_name,
158        crate_name,
159        crate_module,
160        crate_version,
161        rustc_version,
162        default_to_none,
163    } = TypeQueryArguments::from_derive_input(&derived).unwrap();
164
165    let DeriveInput {
166        ident,
167        attrs: _,
168        vis: _,
169        generics,
170        data: _,
171    } = derived;
172    let generics = add_trait_bounds(generics);
173    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
174
175    let type_name = type_name
176        .map(|x| x.to_string())
177        .unwrap_or_else(|| ident.to_string());
178    let crate_name = if let Some(crate_name) = crate_name {
179        quote!(Some(stringify!(#crate_name).to_owned()))
180    } else if default_to_none {
181        quote!(None)
182    } else {
183        quote!(Some(env!("CARGO_PKG_NAME").to_owned()))
184    };
185    let module_path_import;
186    let crate_module = if let Some(crate_module) = crate_module {
187        module_path_import = quote! {};
188        quote!(Some(#crate_module.to_owned()))
189    } else if default_to_none {
190        module_path_import = quote! {};
191        quote!(None)
192    } else {
193        module_path_import = quote! {use ::std::module_path as std_module_path_module_path;};
194        quote!(Some(std_module_path_module_path!().to_owned()))
195    };
196    let crate_version = if let Some(crate_version) = crate_version {
197        quote!(Some(
198                <::typenaming::Version as ::std::str::FromStr>::from_str(
199                #crate_version
200            )
201            .expect(&format!("Failed to parse crate version argument '{}'", #crate_version)))
202        )
203    } else if default_to_none {
204        quote!(None)
205    } else {
206        quote!(Some(::typenaming::new_semver_version(
207            env!("CARGO_PKG_VERSION_MAJOR"),
208            env!("CARGO_PKG_VERSION_MINOR"),
209            env!("CARGO_PKG_VERSION_PATCH"),
210            env!("CARGO_PKG_VERSION_PRE")
211        )))
212    };
213    let rustc_version = if let Some(rustc_version) = rustc_version {
214        quote!(Some(
215            <::typenaming::Version as ::std::str::FromStr>::from_str(
216                #rustc_version
217            )
218            .expect(&format!("Failed to parse rustc version argument '{}'", #rustc_version))
219        ))
220    } else if default_to_none {
221        quote!(None)
222    } else {
223        quote!(Some(
224            ::typenaming::rustc_version().expect("Failed to fetch rustc version")
225        ))
226    };
227    let generics = generics
228        .type_params()
229        .map(|x| x.ident.clone())
230        .collect::<Vec<_>>();
231    let generics = quote!(#(<#generics as ::typenaming::TypeNameable>::type_info()),*);
232    let body = quote! {
233        #module_path_import
234        ::typenaming::TypeInfo::new(
235            #type_name.to_owned(),
236            #crate_name,
237            #crate_module,
238            #crate_version,
239            #rustc_version,
240            vec![
241              #generics
242            ]
243        )
244    };
245    quote! {
246        #[automatically_derived]
247        impl #impl_generics TypeQueryable for #ident #ty_generics #where_clause {
248            fn type_info(&self) -> ::typenaming::TypeInfo {
249                #body
250            }
251        }
252    }
253    .into()
254}