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}