fields_iter_macros/
lib.rs1#![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 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 #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}