Skip to main content

type_name_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!(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}