enum-helper-derive 0.2.0

Procedural macro implementation for enum-helper
Documentation
use quote::ToTokens as _;

use crate::{
    attr::{self, Attr as _, AttrBool, AttrVal, AttrVec},
    ctxt::Ctxt,
    enum_str::{cases::RenameRule, error_msg::ErrorMsgVar},
    template::{TemplateSegment, parse_template},
};

pub struct EnumAttr {
    pub rename_all: AttrVal<RenameRule>,
    pub alias_all: AttrVec<RenameRule>,
    pub default: AttrBool,
    pub error_name: AttrVal<syn::Ident>,
    pub error_msg: AttrVal<Vec<TemplateSegment<ErrorMsgVar>>>,
    pub no_rendering: AttrBool,
    pub no_parsing: AttrBool,
    pub no_error_struct: AttrBool,
}

pub struct VariantAttr {
    pub rename: AttrVal<syn::LitStr>,
    pub alias: AttrVec<syn::LitStr>,
    pub skip: AttrBool,
}

impl EnumAttr {
    pub fn from_attrs(cx: &Ctxt, attrs: &[syn::Attribute]) -> Self {
        use crate::symbol::*;

        let mut ret = Self {
            rename_all: AttrVal::new(ENUM_STR, RENAME_ALL),
            alias_all: AttrVec::new(ENUM_STR, ALIAS_ALL),
            default: AttrBool::new(ENUM_STR, DEFAULT),
            error_name: AttrVal::new(ENUM_STR, ERROR_NAME),
            error_msg: AttrVal::new(ENUM_STR, ERROR_MSG),
            no_rendering: AttrBool::new(ENUM_STR, NO_RENDERING),
            no_parsing: AttrBool::new(ENUM_STR, NO_PARSING),
            no_error_struct: AttrBool::new(ENUM_STR, NO_ERROR_STRUCT),
        };

        for attr in attrs {
            if attr.path() != ENUM_STR {
                continue;
            }

            if let syn::Meta::List(meta) = &attr.meta {
                if meta.tokens.is_empty() {
                    continue;
                }
            }

            let res = attr.parse_nested_meta(|meta| {
                if meta.path == ret.rename_all.name() {
                    let p = |s: syn::LitStr| RenameRule::from_str(&s.value());
                    ret.rename_all.try_from_meta(cx, &meta, p);
                } else if meta.path == ret.alias_all.name() {
                    let p = |s: syn::LitStr| RenameRule::from_str(&s.value());
                    ret.alias_all.try_from_meta(cx, &meta, p);
                } else if meta.path == ret.default.name() {
                    ret.default.try_from_meta(cx, &meta);
                } else if meta.path == ret.error_name.name() {
                    let p = |i: syn::Ident| Result::<_, &str>::Ok(i);
                    ret.error_name.try_from_meta(cx, &meta, p);
                } else if meta.path == ret.error_msg.name() {
                    let p = |s: syn::LitStr| parse_template(&s.value());
                    ret.error_msg.try_from_meta(cx, &meta, p);
                } else if meta.path == ret.no_rendering.name() {
                    ret.no_rendering.try_from_meta(cx, &meta);
                } else if meta.path == ret.no_parsing.name() {
                    ret.no_parsing.try_from_meta(cx, &meta);
                } else if meta.path == ret.no_error_struct.name() {
                    ret.no_error_struct.try_from_meta(cx, &meta);
                } else {
                    let path = meta.path.to_token_stream().to_string().replace(' ', "");
                    let msg = format!("unknown {} container attribute `{}`", ENUM_STR, path);
                    return Err(meta.error(msg));
                };

                Ok(())
            });

            if let Err(err) = res {
                cx.syn_error(err);
            }
        }

        ret.alias_all.check_dup_val(cx);

        attr::check_conflict(cx, &ret.error_msg, &ret.no_error_struct);
        attr::check_conflict(cx, &ret.error_msg, &ret.no_parsing);

        attr::check_conflict(cx, &ret.no_rendering, &ret.no_parsing);

        ret
    }
}

impl VariantAttr {
    pub fn from_attrs(cx: &Ctxt, attrs: &[syn::Attribute]) -> Self {
        use crate::symbol::*;

        let mut ret = Self {
            rename: AttrVal::new(ENUM_STR, RENAME),
            alias: AttrVec::new(ENUM_STR, ALIAS),
            skip: AttrBool::new(ENUM_STR, SKIP),
        };

        for attr in attrs {
            if attr.path() != ENUM_STR {
                continue;
            }

            if let syn::Meta::List(meta) = &attr.meta {
                if meta.tokens.is_empty() {
                    continue;
                }
            }

            let res = attr.parse_nested_meta(|meta| {
                if meta.path == ret.rename.name() {
                    let p = |s: syn::LitStr| Result::<_, &str>::Ok(s);
                    ret.rename.try_from_meta(cx, &meta, p);
                } else if meta.path == ret.alias.name() {
                    let p = |s: syn::LitStr| Result::<_, &str>::Ok(s);
                    ret.alias.try_from_meta(cx, &meta, p);
                } else if meta.path == ret.skip.name() {
                    ret.skip.try_from_meta(cx, &meta);
                } else {
                    let path = meta.path.to_token_stream().to_string().replace(' ', "");
                    let msg = format!("unknown {} variant attribute `{}`", ENUM_STR, path);
                    return Err(meta.error(msg));
                };

                Ok(())
            });

            if let Err(err) = res {
                cx.syn_error(err);
            }
        }

        ret.alias.check_dup_val(cx);

        ret
    }
}