type_registry_register_macro/
lib.rs

1mod register_input;
2mod register_attr;
3mod crate_attribute;
4
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::parse_macro_input;
8use crate::register_attr::RegisterAttr;
9use crate::register_input::RegisterInput;
10
11/// Implements the Registered trait for a type and registers the type with the given
12/// type-registry. Can be applied to structs, enums and unions.
13///
14/// ```
15/// use std::fmt::Debug;
16/// use type_registry::Registry;
17/// use type_registry_register_macro::register;
18///
19/// struct MyTypeInfo {
20///     very_important_info: usize
21/// }
22///
23/// impl MyTypeInfo {
24///     // The macro assumes that the type-info type has a const, no-argument `new` fn like this,
25///     // which is generic on the type being registered (the generic parameter can have bounds,
26///     // like the implicit `Sized` bound here.
27///     pub const fn new<T>() -> Self {
28///         Self {
29///             very_important_info: std::mem::size_of::<T>()
30///         }
31///     }
32///
33///     // If this is not the case, or custom initialisation is needed for some other reason, the
34///     // initialisation can be overridden.
35///     pub const fn new_unsized(number: usize) -> Self {
36///         Self {
37///             very_important_info: number
38///         }
39///     }
40/// }
41///
42/// struct MyRegistry;
43///
44/// impl Registry for MyRegistry {
45///     type TypeInfo = MyTypeInfo;
46///
47///     fn name() -> &'static str {
48///         "My Registry"
49///     }
50/// }
51///
52/// #[register(MyRegistry, MyTypeInfo::new_unsized(42))]
53/// struct MyStruct([u8]);
54///
55/// #[register(MyRegistry)]
56/// enum MyEnum {}
57///
58/// pub mod reexport {
59///     pub use type_registry as type_registry_reexported;
60/// }
61///
62/// #[register(MyRegistry)]
63/// // If the macro has been exported from a non-standard path, use this attribute to customise it
64/// #[type_registry(crate = reexport::type_registry_reexported)]
65/// union MyUnion { u8: u8, u16: u16 }
66/// ```
67#[proc_macro_attribute]
68pub fn register(attr: TokenStream, input: TokenStream) -> TokenStream {
69    let mut input = parse_macro_input!(input as RegisterInput);
70
71    let generics = input.generics();
72    let generic_tokens = quote! { #generics };
73    if !generic_tokens.is_empty() {
74        return syn::Error::new_spanned(
75            generic_tokens,
76            "registration does not support generics"
77        ).into_compile_error().into()
78    }
79
80    let crate_ = match input.crate_() {
81        Ok(path) => path,
82        Err(error) => return error.into_compile_error().into()
83    };
84    let ident = input.ident();
85
86    let attr = parse_macro_input!(attr as RegisterAttr);
87
88    let (
89        registry,
90        init_type_info_expr
91    ) = attr.into_parts(ident, &crate_);
92
93    quote!(
94        #input
95        
96        unsafe impl #crate_::Registered<#registry> for #ident {
97            fn register() -> #crate_::Registration<#registry, Self> {
98                 #crate_::registration!(#registry, #ident)
99            }
100
101            fn type_info() -> &'static <#registry as #crate_::Registry>::TypeInfo {
102                 static TYPE_INFO: <#registry as #crate_::Registry>::TypeInfo = #init_type_info_expr;
103                 &TYPE_INFO
104            }
105        }
106    ).into()
107}