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}