accessory 2.1.0

A configurable get/set/get_mut derive macro
Documentation
use macroific::prelude::*;
use proc_macro2::{Ident, Punct, TokenStream};
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{Error, Expr, LitStr, Token, Visibility, WherePredicate};

#[derive(AttributeOptions)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub struct ContainerOptions {
    pub get: bool,
    pub get_mut: bool,
    pub set: bool,

    pub defaults: ContainerDefaults,
    pub bounds: Punctuated<WherePredicate, Token![,]>,
}

#[derive(AttributeOptions)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub struct FieldOptions {
    pub skip: bool,
    pub all: Option<VariationOptions>,
    pub get: Option<VariationOptions>,
    pub get_mut: Option<VariationOptions>,
    pub set: Option<VariationOptions>,
}

#[derive(ParseOption, Default)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub struct ContainerDefaults {
    pub all: VariationDefaults,
    pub get: VariationDefaults,
    pub get_mut: VariationDefaults,
    pub set: VariationDefaults,
}

#[derive(ParseOption, Clone)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub struct VariationOptions {
    pub owned: Option<bool>,
    pub const_fn: Option<bool>,
    pub skip: Option<bool>,
    pub cp: Option<bool>,
    pub as_ref: Option<bool>,
    pub ptr_deref: Option<DerefKind>,
    pub ty: Option<syn::Type>,
    pub prefix: Option<SkippableIdent>,
    pub suffix: Option<SkippableIdent>,
    pub vis: Option<Visibility>,
    pub bounds: Punctuated<WherePredicate, Token![,]>,
}

#[derive(ParseOption, Default)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub struct VariationDefaults {
    pub owned: Option<bool>,
    pub const_fn: Option<bool>,
    pub cp: Option<bool>,
    pub as_ref: Option<bool>,
    pub ptr_deref: Option<DerefKind>,
    pub prefix: Option<SkippableIdent>,
    pub suffix: Option<SkippableIdent>,
    pub vis: Option<Visibility>,
    pub bounds: Punctuated<WherePredicate, Token![,]>,
}

impl FromExpr for VariationDefaults {
    fn from_expr(expr: Expr) -> syn::Result<Self> {
        Err(Error::new_spanned(
            expr,
            "VariationDefaults can't be constructed from an expression",
        ))
    }
}

impl From<&VariationDefaults> for VariationOptions {
    fn from(defaults: &VariationDefaults) -> Self {
        Self {
            owned: defaults.owned,
            const_fn: defaults.const_fn,
            skip: None,
            cp: defaults.cp,
            as_ref: defaults.as_ref,
            ptr_deref: defaults.ptr_deref,
            ty: None,
            prefix: defaults.prefix.clone(),
            suffix: defaults.suffix.clone(),
            vis: defaults.vis.clone(),
            bounds: defaults.bounds.clone(),
        }
    }
}

macro_rules! assign_defaults {
    (cp $from: ident on $self: ident => $($prop: ident),+ $(,)?) => {
        $(
            if $self.$prop.is_none() {
                if let Some(default_val) = $from.$prop {
                    $self.$prop = Some(default_val);
                }
            }
        )+
    };
    (clone $from: ident on $self: ident => $($prop: ident),+ $(,)?) => {
        $(
            if $self.$prop.is_none() {
                if let Some(ref default_val) = $from.$prop {
                    $self.$prop = Some(default_val.clone());
                }
            }
        )+
    };
    ($from: ident on $self: ident) => {
        assign_defaults!(cp $from on $self => owned, const_fn, cp, ptr_deref);
        assign_defaults!(clone $from on $self => prefix, suffix, vis);
        $self.apply_default_bounds(&$from.bounds);
    };
}

impl VariationOptions {
    pub fn assign_defaults_from_struct(&mut self, defaults: &VariationDefaults) {
        assign_defaults!(defaults on self);
    }

    pub fn assign_defaults_from_prop_all(&mut self, defaults: Option<&Self>) {
        if let Some(defaults) = defaults {
            assign_defaults!(defaults on self);
        }
    }

    fn apply_default_bounds(&mut self, default_bounds: &Punctuated<WherePredicate, Token![,]>) {
        if self.bounds.is_empty() && !default_bounds.is_empty() {
            self.bounds.clone_from(default_bounds);
        }
    }
}

#[derive(Copy, Clone)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub enum DerefKind {
    Auto,
    Deref,
    DerefMut,
}

impl Parse for DerefKind {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.is_empty() {
            Ok(Self::Auto)
        } else if input.peek(Token![mut]) {
            input.parse::<Token![mut]>()?;
            Ok(Self::DerefMut)
        } else if input.peek(Token![ref]) {
            input.parse::<Token![ref]>()?;
            Ok(Self::Deref)
        } else {
            Err(input.error("Expected `mut`, `ref` or nothing"))
        }
    }
}

impl FromExpr for DerefKind {
    fn from_expr(expr: Expr) -> syn::Result<Self> {
        match expr {
            Expr::Reference(expr) => Ok(if expr.mutability.is_some() {
                Self::DerefMut
            } else {
                Self::Deref
            }),
            Expr::Verbatim(tokens) => syn::parse2(tokens),
            other => Err(Error::new_spanned(
                other,
                "Expected `mut`, `ref` or nothing",
            )),
        }
    }
}

impl DerefKind {
    pub fn try_into_tokens(self) -> Option<TokenStream> {
        match self {
            Self::Auto => None,
            Self::Deref => Some(Punct::new_joint('&').into_token_stream()),
            Self::DerefMut => Some(quote! { &mut }),
        }
    }
}

#[derive(Clone)]
#[cfg_attr(feature = "_debug", derive(Debug))]
pub enum SkippableIdent {
    Ident(Ident),
    Skip,
}

impl FromExpr for SkippableIdent {
    fn from_expr(expr: Expr) -> syn::Result<Self> {
        Ok(Self::Ident(Ident::from_expr(expr)?))
    }
}

impl Parse for SkippableIdent {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.peek(syn::Lit) {
            let lit: LitStr = input.parse()?;
            if lit.value().is_empty() {
                Ok(Self::Skip)
            } else {
                Err(Error::new_spanned(lit, "Expected empty string"))
            }
        } else {
            Ok(Self::Ident(input.parse()?))
        }
    }
}

const _: () = {
    use quote::IdentFragment;
    use std::fmt::{Display, Formatter, Result, Write};

    #[derive(Copy, Clone)]
    struct Renderer<'a> {
        ident: &'a SkippableIdent,
        is_prefix: bool,
    }

    impl IdentFragment for Renderer<'_> {
        #[inline]
        fn fmt(&self, f: &mut Formatter) -> Result {
            Display::fmt(self, f)
        }
    }

    impl Display for Renderer<'_> {
        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
            const CHAR: char = '_';

            match self.ident {
                SkippableIdent::Skip => Ok(()),
                SkippableIdent::Ident(ident) => {
                    if self.is_prefix {
                        Display::fmt(ident, f)?;
                        f.write_char(CHAR)
                    } else {
                        f.write_char(CHAR)?;
                        Display::fmt(ident, f)
                    }
                }
            }
        }
    }

    impl SkippableIdent {
        #[inline]
        pub fn as_suffix(&self) -> impl Display + IdentFragment + Copy + '_ {
            Renderer {
                ident: self,
                is_prefix: false,
            }
        }

        #[inline]
        pub fn as_prefix(&self) -> impl Display + IdentFragment + Copy + '_ {
            Renderer {
                ident: self,
                is_prefix: true,
            }
        }
    }
};

macro_rules! parse_opt {
    ($($for: ty),+ $(,)?) => {
        $(
          impl ParseOption for $for {
              #[inline]
              fn from_stream(input: ParseStream) -> syn::Result<Self> {
                  input.parse()
              }
          }
        )+
    };
}

parse_opt!(SkippableIdent, DerefKind);