typekin 0.7.0

new-type pattern with concept of Friends borrowed from c++, no-std and fully const
Documentation
use crate::integral::cfg::IntegralCfg;
use crate::runner;
use crate::runner::mk_flags;
use crate::type_friendship::FriendReq;
use syn::bracketed;
use syn::parse::Parse;
use syn::parse::ParseStream;

mk_flags! {
    #[flag_default(bool=true, str="")]
    #[derive(Debug, Clone)]
    pub(crate) struct BitFlags {
        pub make_value: bool,
        pub impl_value: bool,

        pub value_name: String,
        pub value_name_suffix: String = "Value",

        pub trait_seal: String = "BitSeal",
        pub trait_friend_make: String = "BitFriendMake",
        pub trait_friend_math: String = "BitFriendMath",
        pub trait_friend_bit: String = "BitFriendBit",
        pub trait_friend_rel: String = "BitFriendRel",
    }
}

#[derive(Default)]
pub(crate) struct BitflagCfg {
    pub(crate) friends: Vec<FriendReq>,
    pub(crate) bit: Box<BitFlags>,
    pub(crate) int: Box<IntegralCfg>,
}

impl Parse for BitflagCfg {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut this = Self::default();
        let mut has_integral = false;

        runner::parse_inner_attributes(input, |attr, rest| {
            match attr {
                "with" => this.bit.parse_from(rest, true)?,
                "without" => this.bit.parse_from(rest, false)?,
                "friends" => this.friends = runner::list(rest)?.collect(),
                "value_name_suffix" => {
                    let as_str: syn::LitStr = input.parse()?;
                    if as_str.value().is_empty() {
                        this.bit.value_name_suffix = "".to_string();
                    }
                    else {
                        let as_idn: syn::Ident =
                            syn::parse_str(&as_str.value())?;
                        this.bit.value_name_suffix = as_idn.to_string();
                    }
                }
                "value_name" => {
                    let as_str: syn::LitStr = input.parse()?;
                    let as_idn: syn::Ident = syn::parse_str(&as_str.value())?;
                    this.bit.value_name = as_idn.to_string();
                }
                "integral" => {
                    let content;
                    let _ = bracketed!(content in input);
                    let cfg = IntegralCfg::parse(&content)?;
                    this.int = Box::new(cfg);
                    has_integral = true;
                }
                _ => {}
            };

            return Ok(true);
        })?;

        if !has_integral {
            return Err(syn::Error::new(
                input.span(),
                "missing required `konst` argument in `integral = [...]`",
            ));
        }

        return Ok(this);
    }
}

#[cfg(test)]
mod tests {
    use super::BitflagCfg;

    #[test]
    fn accepts_explicit_nested_konst() {
        let config = syn::parse_str::<BitflagCfg>("integral = [konst = false]")
            .expect("explicit nested konst should parse");
        assert!(!config.int.konst);
    }

    #[test]
    fn rejects_missing_konst() {
        let error = match syn::parse_str::<BitflagCfg>("friends = [u8]") {
            Ok(_) => panic!("konst must be explicit"),
            Err(error) => error,
        };
        assert_eq!(
            error.to_string(),
            "missing required `konst` argument in `integral = [...]`",
        );
    }
}