document_validator_core 0.1.2

Required dependencies for document_validator and document_validator_macros
Documentation
use syn::{
    parse::{Parse, ParseStream},
    Error, Lit, Result, Token, Attribute,
};

pub mod document;

pub trait Merge {
    fn merge(acc: Self, cur: Self) -> Self;
}

pub fn parse_attrs<T>(attrs: &[Attribute], name: &str) -> Result<Option<T>>
    where T: for<'a> TryFrom<&'a Attribute, Error = Error> + Merge
{
    Ok(attrs
        .iter()
        .filter(|x| x.path().is_ident(name))
        .map(T::try_from)
        .collect::<Result<Vec<_>>>()?
        .into_iter()
        .reduce(T::merge))
}

pub fn parse_assign<T>(input: ParseStream) -> Result<T>
where
    T: Parse,
{
    input.parse::<Token![=]>()?;
    match Lit::parse(input)? {
        Lit::Str(string) => string.parse::<T>(),
        other => Err(Error::new(other.span(), "expected string")),
    }
}

pub(crate) use attr_macros::impl_parse;
mod attr_macros {
    macro_rules! impl_parse {
        ($ty: ident ($input: ident, $output: ident) { $($key: pat => $block: block),* $(,)? }) => {
            impl TryFrom<&syn::Attribute> for $ty {
                type Error = syn::Error;

                fn try_from(attr: &syn::Attribute) -> Result<Self, Self::Error> {
                    attr.parse_args()
                }
            }

            impl syn::parse::Parse for $ty {
                fn parse($input: syn::parse::ParseStream) -> syn::Result<Self> {
                    let mut $output = $ty::default();

                    loop {
                        let span = $input.span();
                        let key: syn::Ident = $input.call(syn::ext::IdentExt::parse_any)?;

                        match key.to_string().as_str() {
                            $($key => $block,)*
                            x => {
                                $crate::error::syn_error!(
                                    span,
                                    r#"Unknown attribute "{x}". Accepted attributes are: {}"#,
                                    [$(stringify!($key)),*].join(", "),
                                );
                            },
                        };

                        if $input.is_empty() {
                            break;
                        }

                        $input.parse::<syn::Token![,]>()?;
                    }

                    Ok($output)
                }
            }
        };
    }

    pub(crate) use impl_parse;
}