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!(TypeName));
9 }
10 }
11 generics
12}
13
14use darling::FromDeriveInput;
15
16#[derive(FromDeriveInput, Debug)]
17#[darling(attributes(typename))]
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_version: Option<String>,
25 #[darling(default)]
26 rustc_version: Option<String>,
27 #[darling(default)]
28 default_to_none: bool,
29}
30
31#[proc_macro_derive(TypeName, attributes(typename))]
32pub fn derive_type_name(tokens: TokenStream) -> TokenStream {
33 let derived = parse_macro_input!(tokens);
34 let TypeNameArguments {
35 type_name,
36 crate_name,
37 crate_version,
38 rustc_version,
39 default_to_none,
40 } = TypeNameArguments::from_derive_input(&derived).unwrap();
41 let DeriveInput {
42 ident,
43 attrs: _,
44 vis: _,
45 generics,
46 data: _,
47 } = derived;
48 let generics = add_trait_bounds(generics);
49 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
50
51 let type_name = type_name
52 .map(|x| x.to_string())
53 .unwrap_or_else(|| ident.to_string());
54 let crate_name = if let Some(crate_name) = crate_name {
55 quote!(Some(stringify!(#crate_name).to_owned()))
56 } else if default_to_none {
57 quote!(None)
58 } else {
59 quote!(Some(env!("CARGO_PKG_NAME").to_owned()))
60 };
61 let crate_version = if let Some(crate_version) = crate_version {
62 quote!(Some(
63 <::type_name::TypeNameSemverVersion as ::std::str::FromStr>::from_str(
64 #crate_version
65 )
66 .expect(&format!("Failed to parse crate version argument '{}'", #crate_version)))
67 )
68 } else if default_to_none {
69 quote!(None)
70 } else {
71 quote!(Some(::type_name::new_semver_version(
72 env!("CARGO_PKG_VERSION_MAJOR"),
73 env!("CARGO_PKG_VERSION_MINOR"),
74 env!("CARGO_PKG_VERSION_PATCH"),
75 env!("CARGO_PKG_VERSION_PRE")
76 )))
77 };
78 let rustc_version = if let Some(rustc_version) = rustc_version {
79 quote!(Some(
80 <::type_name::TypeNameSemverVersion as ::std::str::FromStr>::from_str(
81 #rustc_version
82 )
83 .expect(&format!("Failed to parse rustc version argument '{}'", #rustc_version))
84 ))
85 } else if default_to_none {
86 quote!(None)
87 } else {
88 quote!(Some(
89 ::type_name::rustc_version().expect("Failed to fetch rustc version")
90 ))
91 };
92 let generics = generics
93 .type_params()
94 .map(|x| x.ident.clone())
95 .collect::<Vec<_>>();
96 let generics = quote!(#(<#generics as ::type_name::TypeName>::type_name_static()),*);
97 let body = quote! {
98 ::type_name::TypeNameData::new(
99 #type_name.to_owned(),
100 #crate_name,
101 #crate_version,
102 #rustc_version,
103 vec![
104 #generics
105 ]
106 )
107 };
108 quote! {
109 #[automatically_derived]
110 impl #impl_generics TypeName for #ident #ty_generics #where_clause {
111 fn type_name(&self) -> ::type_name::TypeNameData {
112 #body
113 }
114 fn type_name_static() -> ::type_name::TypeNameData where Self: Sized {
115 #body
116 }
117 }
118 }
119 .into()
120}