opt_args 2.0.0

Create macros for functions and structs with default values
Documentation
use deluxe::ExtractAttributes;
use derive_syn_parse::Parse;
use proc_macro2::{Ident, Span};
use syn::{
    parse::{Parse, ParseStream},
    token::{Async, Brace, Colon, Const, Paren, Semi, Struct, Unsafe},
    Abi, Attribute, Block, Error, Expr, Generics, ReturnType, Token, Type, Visibility,
};

#[derive(Parse, Clone)]
pub(crate) struct OptArgsItem {
    #[call(Attribute::parse_outer)]
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub constness: Option<Const>,
    pub asyncness: Option<Async>,
    pub unsafety: Option<Unsafe>,
    pub abi: Option<Abi>,
    pub item: OptArgsItemType,
}

#[derive(Clone)]
pub(crate) enum OptArgsItemType {
    ItemFn(OptArgsItemFn),
    ItemStruct(OptArgsItemStruct),
}

impl OptArgsItemType {
    pub fn ident(&self) -> &Ident {
        match self {
            OptArgsItemType::ItemFn(item_fn) => &item_fn.ident,
            OptArgsItemType::ItemStruct(item_struct) => &item_struct.ident,
        }
    }
}

impl Parse for OptArgsItemType {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.peek(Token![fn]) {
            Ok(Self::ItemFn(input.parse()?))
        } else if input.peek(Token![struct]) {
            Ok(Self::ItemStruct(input.parse()?))
        } else {
            Err(Error::new(
                Span::call_site(),
                "`opt_args` can only be applied to functions or structs",
            ))
        }
    }
}

#[derive(Parse, Clone)]
pub(crate) struct OptArgsItemFn {
    _fn_token: Token![fn],
    pub ident: Ident,
    pub generics: Generics,
    #[paren]
    _paren_token: Paren,
    #[inside(_paren_token)]
    #[call(parse_vector)]
    pub inputs: Vec<OptArgsItemFnArg>,
    pub output: ReturnType,
    pub block: Box<Block>,
}

#[derive(Parse, Clone)]
pub(crate) struct OptArgsItemFnArg {
    #[call(Attribute::parse_outer)]
    pub attrs: Vec<Attribute>,
    pub ident: Ident,
    _colon_token: Colon,
    pub ty: Box<Type>,
    _eq: Option<Token![=]>,
    #[parse_if(_eq.is_some())]
    pub value: Option<Expr>,
    #[parse_if(_eq.is_none())]
    pub default: Option<Option<Token![?]>>,
}

#[derive(Parse, Clone)]
pub(crate) struct OptArgsItemStruct {
    _struct_token: Struct,
    pub ident: Ident,
    pub generics: Generics,
    #[brace]
    _paren_token: Brace,
    #[inside(_paren_token)]
    #[call(parse_vector)]
    pub fields: Vec<OptArgsItemStructFields>,
    pub semi_token: Option<Semi>,
}

#[derive(Parse, Clone)]
pub(crate) struct OptArgsItemStructFields {
    #[call(Attribute::parse_outer)]
    pub attrs: Vec<Attribute>,
    pub vis: Visibility,
    pub mutability: Option<Token![mut]>,
    pub ident: Ident,
    _colon_token: Colon,
    pub ty: Type,
    _eq: Option<Token![=]>,
    #[parse_if(_eq.is_some())]
    pub value: Option<Expr>,
    #[parse_if(_eq.is_none())]
    pub default: Option<Option<Token![?]>>,
}

fn parse_vector<T: Parse>(input: ParseStream) -> syn::Result<Vec<T>> {
    Ok(input
        .parse_terminated(T::parse, Token![,])?
        .into_iter()
        .collect())
}

#[derive(Clone)]
pub(crate) struct GenericOptArg {
    pub ident: Ident,
    pub ty: Box<Type>,
    pub value: Option<Expr>,
    pub default: bool,
}

impl GenericOptArg {
    pub fn is_optional(&self) -> bool {
        self.value.is_some() || self.default
    }
}

impl From<OptArgsItemFnArg> for GenericOptArg {
    fn from(arg: OptArgsItemFnArg) -> Self {
        Self {
            ident: arg.ident,
            ty: arg.ty,
            value: arg.value,
            default: matches!(arg.default, Some(Some(_))),
        }
    }
}

impl From<OptArgsItemStructFields> for GenericOptArg {
    fn from(arg: OptArgsItemStructFields) -> Self {
        Self {
            ident: arg.ident,
            ty: Box::new(arg.ty),
            value: arg.value,
            default: matches!(arg.default, Some(Some(_))),
        }
    }
}

#[derive(ExtractAttributes, Debug)]
#[deluxe(attributes(opt_args))]
pub(crate) struct OptArgsAttributes {
    pub shuffle: Option<()>,
    pub non_export: Option<()>,
    pub rename: Option<Ident>,
}