attrimpl 0.5.0

The crate provides attributes to automatically implement common traits and reduce boilerplate code.
Documentation
#[derive(Copy, Clone, Default)]
pub enum GetRefType {
    #[default]
    Ref,
    Copy,
    Clone,
}

#[derive(Clone)]
pub struct AccessParams {
    pub get_name: syn::Ident,
    pub get_mut_name: syn::Ident,
    pub get_ref_type: GetRefType,
}

impl AccessParams {
    pub fn parse(
        input: syn::parse::ParseStream,
        default_name: Option<syn::Ident>,
    ) -> syn::Result<Self> {
        fn set_get_ref_type(
            existing: &mut Option<GetRefType>,
            new: GetRefType,
            span: proc_macro2::Span,
        ) -> syn::Result<()> {
            if existing.is_some() {
                return Err(syn::Error::new(span, "get_ref_type already specified"));
            }
            *existing = Some(new);
            Ok(())
        }

        fn set_name(
            existing: &mut Option<syn::Ident>,
            new: syn::LitStr,
            span: proc_macro2::Span,
        ) -> syn::Result<()> {
            if existing.is_some() {
                return Err(syn::Error::new(span, "name already specified"));
            }
            *existing = Some(syn::Ident::new(&new.value(), new.span()));
            Ok(())
        }

        let mut name = None;
        let mut get_ref_type = None;

        if input.peek(syn::token::Paren) {
            let content;
            syn::parenthesized!(content in input);

            while !content.is_empty() {
                let ident: syn::Ident = content.parse()?;

                match ident.to_string().as_str() {
                    "get_clone" => {
                        set_get_ref_type(&mut get_ref_type, GetRefType::Clone, ident.span())?;
                    }
                    "get_copy" => {
                        set_get_ref_type(&mut get_ref_type, GetRefType::Copy, ident.span())?;
                    }
                    "get_ref" => {
                        set_get_ref_type(&mut get_ref_type, GetRefType::Ref, ident.span())?;
                    }
                    "name" => {
                        content.parse::<syn::Token![=]>()?;
                        let name_lit: syn::LitStr = content.parse()?;
                        set_name(&mut name, name_lit, ident.span())?;
                    }
                    other => {
                        return Err(syn::Error::new(
                            ident.span(),
                            format!(
                                "expected one of `get_ref`, `get_copy`, `get_clone`, or `name = <..>`, found `{}`",
                                other
                            ),
                        ));
                    }
                }

                if content.is_empty() {
                    break;
                }

                content.parse::<syn::Token![,]>()?;
            }
        }

        let get_name = match name {
            Some(name) => name,
            None => default_name.ok_or_else(|| {
                syn::Error::new(
                    input.span(),
                    "name must be specified if no default name is provided",
                )
            })?,
        };
        let get_mut_name = syn::Ident::new(&format!("{}_mut", get_name), get_name.span());

        Ok(AccessParams {
            get_name,
            get_mut_name,
            get_ref_type: get_ref_type.unwrap_or_default(),
        })
    }
}