field_access_derive 0.1.11

Derive macro for the field_access crate
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::{punctuated::Punctuated, Data, DeriveInput, Error, Field, Result, Token};

pub fn derive(input: &DeriveInput) -> Result<TokenStream> {
    let name = &input.ident;

    let fields: Vec<_> = fields(input)?
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().expect("field has a name");
            let name_str = name.to_string();
            (name, name_str)
        })
        .collect();

    let field_names = fields.iter().map(|(_, name_str)| quote!(#name_str));

    let immutable_arms = fields.iter().map(|(name, name_str)| {
        quote!(#name_str => {
            Some(&self.#name as &dyn ::core::any::Any)
        })
    });

    let mutable_arms = fields.iter().map(|(name, name_str)| {
        quote!(#name_str => {
            Some(&mut self.#name as &mut dyn ::core::any::Any)
        })
    });

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    Ok(quote! {
        impl #impl_generics ::field_access::AnyFieldAccess for #name #ty_generics #where_clause {
            fn field_as_any(&self, field: &str) -> ::core::option::Option<&dyn ::core::any::Any> {
                match field {
                    #(#immutable_arms)*
                    _ => None
                }
            }

            fn field_as_any_mut(&mut self, field: &str) -> ::core::option::Option<&mut dyn ::core::any::Any> {
                match field {
                    #(#mutable_arms)*
                    _ => None
                }
            }

            fn field_names(&self) -> &'static [&'static str] {
                &[#(#field_names),*]
            }
        }
    })
}

type Fields = Punctuated<Field, Token![,]>;

fn fields(input: &DeriveInput) -> Result<&Fields> {
    use syn::Fields;

    let unsupported = match &input.data {
        Data::Struct(data) => match &data.fields {
            Fields::Named(fields) => return Ok(&fields.named),
            Fields::Unnamed(_) => "newtype structs",
            Fields::Unit => "unit structs",
        },
        Data::Enum(_) => "enums",
        Data::Union(_) => "unions",
    };

    Err(Error::new_spanned(
        input,
        format!("FieldAccess does not support {unsupported}"),
    ))
}