introspect_proc_macros/
lib.rs

1//! Procedural macros for `introspect`.
2
3#![warn(missing_docs)]
4#![warn(rust_2018_idioms)]
5#![warn(rust_2021_compatibility)]
6#![warn(missing_debug_implementations)]
7#![warn(rustdoc::broken_intra_doc_links)]
8
9use core::panic;
10
11use introspect_core::r#enum::Variant;
12use introspect_core::r#struct::Field;
13use introspect_core::Entity;
14use introspect_core::Enum;
15use introspect_core::Struct;
16use proc_macro2::TokenStream;
17use quote::quote;
18use syn::Item;
19
20/// The primary `derive` procedural macro that implements the introspection traits.
21#[proc_macro_derive(Introspect)]
22pub fn introspect(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
23    let item = syn::parse_macro_input!(stream as Item);
24
25    match item {
26        Item::Enum(enum_) => parse_item_enum(enum_),
27        Item::Struct(struct_) => parse_item_struct(struct_),
28        _ => {
29            quote! {
30                compile_error!("Introspect can only be derived for `enum`s and `struct`s")
31            }
32        }
33    }
34    .into()
35}
36
37fn parse_item_enum(item: syn::ItemEnum) -> TokenStream {
38    let ident = &item.ident;
39
40    let enum_ = match Enum::try_from(&item) {
41        Ok(enum_) => Entity::Enum(enum_),
42        // SAFETY: this panic is okay because it happens during the compilation
43        // process. As such, the Rust compiler will complain with this error instead
44        // of happening at program runtime.
45        Err(err) => panic!("error: {err}"),
46    };
47
48    let variants = item
49        .variants
50        .iter()
51        .map(|field| match Variant::try_from(field) {
52            Ok(variant) => variant,
53            // SAFETY: this panic is okay because it happens during the compilation
54            // process. As such, the Rust compiler will complain with this error instead
55            // of happening at program runtime.
56            Err(err) => panic!("error: {err}"),
57        })
58        .map(introspect_core::Member::Variant)
59        .collect::<Vec<_>>();
60
61    quote! {
62        #[automatically_derived]
63        impl ::introspect::IntrospectedEntity for #ident {
64
65            fn introspected_entity() -> ::introspect::Entity {
66                #enum_
67            }
68        }
69
70        #[automatically_derived]
71        impl ::introspect::IntrospectedMembers for #ident {
72
73            fn introspected_members() -> Vec<::introspect::Member> {
74                vec![
75                    #(#variants),*
76                ]
77            }
78        }
79
80        #[automatically_derived]
81        impl ::introspect::Introspected for #ident {}
82    }
83}
84
85fn parse_item_struct(item: syn::ItemStruct) -> TokenStream {
86    let ident = &item.ident;
87
88    let struct_ = match Struct::try_from(&item) {
89        Ok(struct_) => Entity::Struct(struct_),
90        // SAFETY: this panic is okay because it happens during the compilation
91        // process. As such, the Rust compiler will complain with this error instead
92        // of happening at program runtime.
93        Err(err) => panic!("error: {err}"),
94    };
95
96    let fields = item
97        .fields
98        .iter()
99        .map(|field| match Field::try_from(field) {
100            Ok(field) => field,
101            // SAFETY: this panic is okay because it happens during the compilation
102            // process. As such, the Rust compiler will complain with this error instead
103            // of happening at program runtime.
104            Err(err) => panic!("error: {err}"),
105        })
106        .map(introspect_core::Member::Field)
107        .collect::<Vec<_>>();
108
109    quote! {
110        #[automatically_derived]
111        impl ::introspect::IntrospectedEntity for #ident {
112
113            fn introspected_entity() -> ::introspect::Entity {
114                #struct_
115            }
116        }
117
118        #[automatically_derived]
119        impl ::introspect::IntrospectedMembers for #ident {
120
121            fn introspected_members() -> Vec<::introspect::Member> {
122                vec![
123                    #(#fields),*
124                ]
125            }
126        }
127
128        #[automatically_derived]
129        impl ::introspect::Introspected for #ident {}
130    }
131}