sark-gen 0.23.0

Sark proc-macro generators
Documentation
use syn::{parse, punctuated};

#[derive(Clone, Copy)]
pub(super) enum JsonKind {
    Unordered,
    Ordered,
}

#[derive(Clone, Copy)]
pub(super) enum Purpose {
    Decode,
    Encode,
    TryEncode,
}

pub(crate) struct Mode {
    pub(super) kind: JsonKind,
    pub(super) exact: bool,
    pub(super) plain: bool,
    pub(super) purpose: Purpose,
}

impl parse::Parse for Mode {
    fn parse(input: parse::ParseStream<'_>) -> syn::Result<Self> {
        use syn::Error;

        let flags = punctuated::Punctuated::<syn::Ident, syn::Token![,]>::parse_terminated(input)?;
        for flag in &flags {
            if flag != "ordered"
                && flag != "unordered"
                && flag != "exact"
                && flag != "plain"
                && flag != "encode"
                && flag != "try_encode"
            {
                return Err(Error::new_spanned(
                    flag,
                    "#[sark_gen::json] supports only `ordered`, `unordered`, `exact`, `plain`, `encode`, or `try_encode`",
                ));
            }
        }
        let kind = if flags
            .iter()
            .rev()
            .find(|flag| *flag == "ordered" || *flag == "unordered")
            .is_some_and(|flag| flag == "ordered")
        {
            JsonKind::Ordered
        } else {
            JsonKind::Unordered
        };
        let exact = flags.iter().find(|flag| *flag == "exact");
        if let Some(flag) = exact
            && !matches!(kind, JsonKind::Ordered)
        {
            return Err(Error::new_spanned(flag, "`exact` requires `ordered`"));
        }
        let mut modes = flags
            .iter()
            .filter(|flag| *flag == "encode" || *flag == "try_encode");
        let purpose = if let Some(flag) = modes.next() {
            if flag == "encode" {
                Purpose::Encode
            } else {
                Purpose::TryEncode
            }
        } else {
            Purpose::Decode
        };
        if let Some(flag) = modes.next() {
            return Err(Error::new_spanned(flag, "encoding mode specified twice"));
        }
        if let Some(flag) = exact
            && !matches!(purpose, Purpose::Decode)
        {
            return Err(Error::new_spanned(
                flag,
                "encoding-only modes cannot be combined with `exact`",
            ));
        }
        Ok(Self {
            kind,
            exact: exact.is_some(),
            plain: flags.iter().any(|flag| flag == "plain"),
            purpose,
        })
    }
}