fields_iter_macros/
lib.rs

1#![forbid(unsafe_code, rust_2018_idioms)]
2
3use quote::{format_ident, quote, quote_spanned};
4use syn::spanned::Spanned;
5
6#[proc_macro_derive(FieldsInspect)]
7pub fn derive_fields_inspect(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
8    let item = syn::parse_macro_input!(item as syn::DeriveInput);
9    let syn::Data::Struct(strukt) = item.data else {
10        return syn::Error::new_spanned(&item, "only structs are supported for `#[derive(FieldsInspect)]`")
11            .into_compile_error()
12            .into();
13    };
14
15    let name = &item.ident;
16    let name_string = name.to_string();
17    let fields = match strukt.fields {
18        syn::Fields::Named(syn::FieldsNamed { named, .. }) => named,
19        syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => unnamed,
20        syn::Fields::Unit => syn::punctuated::Punctuated::new(),
21    };
22    let fields_count = fields.len() as u32;
23    let ((field_names, field_refs), field_muts): ((Vec<_>, Vec<_>), Vec<_>) = fields
24        .iter()
25        .enumerate()
26        .map(|(idx, field)| {
27            let idx = idx as u32;
28            let (name_ident, name_string) = match &field.ident {
29                Some(name) => (name.clone(), name.to_string()),
30                None => (format_ident!("{idx}"), idx.to_string()),
31            };
32            let name = quote!(#idx => #name_string,);
33            // Span to the field type for if the it is not `'static`.
34            let field_ref = quote_spanned! {field.ty.span() =>
35                #idx => &self.#name_ident as &dyn ::core::any::Any,
36            };
37            let field_mut = quote_spanned! {field.ty.span() =>
38                // SAFETY: By precondition, `this` points to a valid `Self`.
39                #idx => unsafe {
40                    &mut *(
41                        ::fields_iter::addr_of_mut!((*this).#name_ident)
42                            as *mut dyn ::core::any::Any
43                    )
44                }
45            };
46            ((name, field_ref), field_mut)
47        })
48        .unzip();
49    let (impl_generics, type_generics, where_clause) = item.generics.split_for_impl();
50    quote! {
51        impl #impl_generics ::fields_iter::FieldsInspectImpl for #name #type_generics
52        #where_clause
53        {
54            fn struct_name() -> &'static str { #name_string }
55            fn fields_count() -> u32 { #fields_count }
56
57            fn field_name(n: u32) -> &'static str {
58                match n {
59                    #(#field_names)*
60                    _ => ::fields_iter::field_out_of_bounds(#name_string, n),
61                }
62            }
63
64            fn field(&self, n: u32) -> &dyn ::core::any::Any {
65                match n {
66                    #(#field_refs)*
67                    _ => ::fields_iter::field_out_of_bounds(#name_string, n),
68                }
69            }
70
71            unsafe fn field_mut(this: *mut (), n: u32) -> &'static mut dyn ::core::any::Any {
72                let this = this.cast::<Self>();
73                match n {
74                    #(#field_muts)*
75                    _ => ::fields_iter::field_out_of_bounds(#name_string, n),
76                }
77            }
78        };
79    }
80    .into()
81}