typekin 0.6.0

new-type pattern with concept of Friends borrowed from c++, no-std and fully const
Documentation
use crate::runner;
use crate::runner::{MkErr, mk_flags};
use crate::type_friendship::FriendReq;
use quote::ToTokens;
use std::fmt::{Debug, Formatter};
use syn::Path;
use syn::parse::{Parse, ParseStream};

mk_flags! {
    #[flag_default(bool=true, str="")]
    #[derive(Debug, Clone)]
    pub(crate) struct IntegralFlags {
        pub impl_range: bool = false,

        pub assertions: bool,

        pub impl_debug: bool,
        pub impl_eq: bool,
        pub impl_fmt_binary: bool,
        pub impl_fmt_hex_lower: bool,
        pub impl_fmt_hex_upper: bool,
        pub impl_fmt_octal: bool,
        pub impl_into: bool,
        pub impl_ord: bool,
        pub impl_partial_eq: bool,
        pub impl_partial_ord: bool,
        pub impl_try_into: bool,

        pub impl_assign_add: bool,
        pub impl_assign_and: bool,
        pub impl_assign_div: bool,
        pub impl_assign_mul: bool,
        pub impl_assign_or: bool,
        pub impl_assign_rem: bool,
        pub impl_assign_shl: bool,
        pub impl_assign_shr: bool,
        pub impl_assign_sub: bool,
        pub impl_math_add: bool,
        pub impl_math_and: bool,
        pub impl_math_div: bool,
        pub impl_math_mul: bool,
        pub impl_math_not: bool,
        pub impl_math_or: bool,
        pub impl_math_rem: bool,
        pub impl_math_shl: bool,
        pub impl_math_shr: bool,
        pub impl_math_sub: bool,
        pub impl_math_xor: bool,

        pub impl_friend: bool,
        pub impl_friend_make: bool,
        pub impl_friend_math_bit: bool,
        pub impl_friend_math_ops: bool,
        pub impl_friend_math_rel: bool,
        pub impl_friend_seal: bool,
        pub impl_friendzone_friend_make: bool,
        pub impl_friendzone_friend_math_bit: bool,
        pub impl_friendzone_friend_math_ops: bool,
        pub impl_friendzone_friend_math_rel: bool,
        pub impl_friendzone_seal: bool,

        pub fn_conv_into: bool,
        pub fn_conv_of: bool,
        pub fn_conv_raw: bool,
        pub fn_conv_try_into_checked: bool,
        pub fn_conv_try_into_unchecked: bool,
        pub fn_make_checked: bool,
        pub fn_make_checked_try: bool,
        pub fn_make_unchecked: bool,
        pub fn_make_unchecked_try: bool,
        pub fn_math_add: bool,
        pub fn_math_and: bool,
        pub fn_math_div: bool,
        pub fn_math_mul: bool,
        pub fn_math_not: bool,
        pub fn_math_or: bool,
        pub fn_math_rem: bool,
        pub fn_math_shl: bool,
        pub fn_math_shr: bool,
        pub fn_math_sub: bool,
        pub fn_math_xor: bool,
        pub fn_op_cmp: bool,
        pub fn_op_eq: bool,

        pub fp_unchecked: String = "Self::_unchecked",

        pub trait_seal: String = "Seal",
        pub trait_friend_make: String = "FriendMake",
        pub trait_friend_math: String = "FriendMath",
        pub trait_friend_bit: String = "FriendBit",
        pub trait_friend_rel: String = "FriendRel",

        pub fn_prefix_seal: String = "into_",
    }
}

#[derive(Default, Clone)]
pub(crate) struct IntegralCfg {
    pub(crate) flags: Box<IntegralFlags>,
    pub(crate) konst: bool,
    pub(crate) fn_get_raw: Option<Path>,
    pub(crate) fn_validator: Option<Path>,
    pub(crate) friends: Vec<FriendReq>,
}

impl Debug for IntegralCfg {
    fn fmt(
        &self,
        f: &mut Formatter<'_>,
    ) -> std::fmt::Result {
        write!(
            f,
            "IntegralCfg[int: {:?}, friends: {:?}, fn_get_raw: {}, fn_validator: {}",
            self.flags,
            self.friends,
            self.fn_get_raw
                .as_ref()
                .map(|it| it.to_token_stream().to_string())
                .unwrap_or_default(),
            self.fn_validator
                .as_ref()
                .map(|it| it.to_token_stream().to_string())
                .unwrap_or_default(),
        )
    }
}

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

        runner::parse_inner_attributes(input, |attr, span, rest| {
            match attr {
                "konst" => {
                    this.konst = rest.parse::<syn::LitBool>()?.value;
                    has_konst = true;
                }
                "with" => this.flags.parse_from(rest, true)?,
                "without" => this.flags.parse_from(rest, false)?,
                "friends" => this.friends = runner::list(rest)?.collect(),
                "fn_get_raw" => this.fn_get_raw = Some(rest.parse()?),
                "fn_validator" => this.fn_validator = Some(rest.parse()?),
                _ => return span.fail("unknown integral arg"),
            };

            return Ok(());
        })?;

        if !has_konst {
            return Err(syn::Error::new(
                input.span(),
                "missing required `konst` argument",
            ));
        }

        return Ok(this);
    }
}

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

    #[test]
    fn accepts_explicit_konst_values() {
        let konst = syn::parse_str::<IntegralCfg>("konst = true")
            .expect("explicit true konst should parse");
        assert!(konst.konst);

        let konst = syn::parse_str::<IntegralCfg>("konst = false")
            .expect("explicit false konst should parse");
        assert!(!konst.konst);
    }

    #[test]
    fn rejects_missing_konst() {
        let error = syn::parse_str::<IntegralCfg>("friends = [u8]")
            .expect_err("konst must be explicit");
        assert_eq!(error.to_string(), "missing required `konst` argument");
    }
}