serde_valid_derive 0.24.0

JSON Schema based validation tool using serde.
Documentation
use crate::attribute::{
    MetaListCustomMessage, MetaListFieldValidation, MetaListStructValidation,
    MetaNameValueCustomMessage, MetaNameValueFieldValidation, MetaNameValueStructValidation,
    MetaPathCustomMessage, MetaPathFieldValidation, MetaPathStructValidation,
};
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;

pub fn object_errors_tokens() -> TokenStream {
    quote!(::serde_valid::validation::Errors::Object(
        ::serde_valid::validation::ObjectErrors::new(
            __rule_vec_errors,
            __property_vec_errors_map
                .into_iter()
                .map(|(field, errors)| {
                    let mut __field_items_errors = vec![];
                    let mut __field_properties_errors = None;
                    let mut __field_errors: ::serde_valid::validation::VecErrors = errors
                        .into_iter()
                        .filter_map(|error| match error {
                            ::serde_valid::validation::Error::Items(__array_errors) => {
                                __field_items_errors.push(__array_errors);
                                None
                            }
                            ::serde_valid::validation::Error::Properties(__object_errors) => {
                                __field_properties_errors = Some(__object_errors);
                                None
                            }
                            _ => Some(error),
                        })
                        .collect();

                    if let Some(__object_errors) = __field_properties_errors {
                        __field_errors.extend(__object_errors.errors);

                        (
                            field,
                            ::serde_valid::validation::Errors::Object(
                                ::serde_valid::validation::ObjectErrors::new(
                                    __field_errors,
                                    __object_errors.properties,
                                ),
                            ),
                        )
                    } else if !__field_items_errors.is_empty() {
                        let __array_errors = __field_items_errors
                            .into_iter()
                            .reduce(|a, b| a.merge(b))
                            .unwrap();
                        __field_errors.extend(__array_errors.errors);

                        (
                            field,
                            ::serde_valid::validation::Errors::Array(
                                ::serde_valid::validation::error::ArrayErrors::new(
                                    __field_errors,
                                    __array_errors.items,
                                ),
                            ),
                        )
                    } else {
                        (
                            field,
                            ::serde_valid::validation::Errors::NewType(__field_errors),
                        )
                    }
                })
                .collect()
        )
    ))
}

pub fn array_errors_tokens() -> TokenStream {
    quote!(::serde_valid::validation::Errors::Array(
        ::serde_valid::validation::error::ArrayErrors::new(
            __rule_vec_errors,
            __item_vec_errors_map
                .into_iter()
                .map(|(index, errors)| {
                    let mut __field_items_errors = vec![];
                    let mut __field_properties_errors = None;
                    let mut __field_errors: ::serde_valid::validation::VecErrors = errors
                        .into_iter()
                        .filter_map(|error| match error {
                            ::serde_valid::validation::Error::Items(__array_errors) => {
                                __field_items_errors.push(__array_errors);
                                None
                            }
                            ::serde_valid::validation::Error::Properties(__object_errors) => {
                                __field_properties_errors = Some(__object_errors);
                                None
                            }
                            _ => Some(error),
                        })
                        .collect();

                    if let Some(__object_errors) = __field_properties_errors {
                        __field_errors.extend(__object_errors.errors);

                        (
                            index,
                            ::serde_valid::validation::Errors::Object(
                                ::serde_valid::validation::ObjectErrors::new(
                                    __field_errors,
                                    __object_errors.properties,
                                ),
                            ),
                        )
                    } else if !__field_items_errors.is_empty() {
                        let __array_errors = __field_items_errors
                            .into_iter()
                            .reduce(|a, b| a.merge(b))
                            .unwrap();
                        __field_errors.extend(__array_errors.errors);

                        (
                            index,
                            ::serde_valid::validation::Errors::Array(
                                ::serde_valid::validation::error::ArrayErrors::new(
                                    __field_errors,
                                    __array_errors.items,
                                ),
                            ),
                        )
                    } else {
                        (
                            index,
                            ::serde_valid::validation::Errors::NewType(__field_errors),
                        )
                    }
                })
                .collect()
        )
    ))
}

pub fn new_type_errors_tokens() -> TokenStream {
    quote!(::serde_valid::validation::Errors::NewType(
        __rule_vec_errors
            .into_iter()
            .chain(
                __item_vec_errors_map
                    .remove(&0)
                    .unwrap_or(vec![])
                    .into_iter()
            )
            .collect()
    ))
}

#[derive(Debug)]
pub struct Error(syn::Error);

impl Error {
    fn new<Message: Into<String>>(span: proc_macro2::Span, message: Message) -> Self {
        Self(syn::Error::new(span, message.into()))
    }

    #[allow(dead_code)]
    pub fn macro_debug<Message: Into<String>>(span: proc_macro2::Span, message: Message) -> Self {
        Error::new(span, message)
    }

    pub fn unit_struct_not_supported(input: &syn::DeriveInput) -> Self {
        Self::new(
            input.span(),
            "#[derive(Validate)] does not support Unit Struct.",
        )
    }

    pub fn union_not_supported(input: &syn::DeriveInput) -> Self {
        Self::new(input.span(), "#[derive(Validate)] does not support Union.")
    }

    pub fn rule_allow_function_call_or_closure(span: impl Spanned) -> Self {
        Self::new(span.span(), "#[rule(???)] allows function call or closure.")
    }

    pub fn rule_allow_single_function(meta: &crate::types::NestedMeta) -> Self {
        Self::new(meta.span(), "#[rule(???)] allows single function.")
    }

    pub fn rule_need_arguments(path: &syn::Path) -> Self {
        Self::new(path.span(), "`rule` function needs arguments.")
    }

    pub fn rule_args_parse_error(meta_list: &syn::MetaList, error: &syn::Error) -> Self {
        Self::new(
            meta_list.span(),
            format!("#[rule(???)] parse error: {error}"),
        )
    }

    pub fn rule_args_allow_field_name(
        rule_fn_name_path: &syn::Path,
        meta: &crate::types::NestedMeta,
    ) -> Self {
        let rule_fn_name = quote!(#rule_fn_name_path).to_string();
        Self::new(
            meta.span(),
            format!("#[rule({rule_fn_name}(???, ...))] allows field name only."),
        )
    }

    pub fn rule_args_allow_field_index(
        rule_fn_name_path: &syn::Path,
        meta: &crate::types::NestedMeta,
    ) -> Self {
        let rule_fn_name = quote!(#rule_fn_name_path).to_string();
        Self::new(
            meta.span(),
            format!("#[rule({rule_fn_name}(???, ...))] allows field index only."),
        )
    }

    pub fn rule_named_clousure_input(meta: &syn::Pat) -> Self {
        Self::new(meta.span(), "Inputs of closure allows filed name only.")
    }

    pub fn rule_unnamed_clousure_input(meta: &syn::Pat) -> Self {
        Self::new(
            meta.span(),
            "Inputs of closure allows field index (like _0, _1, etc...) only.",
        )
    }

    pub fn validate_meta_name_value_not_supported(name_value: &syn::MetaNameValue) -> Self {
        Self::new(name_value.span(), "#[validate = ???] not supported.")
    }

    pub fn meta_path_validation_need_value(path: &syn::Path, validation_type: &str) -> Self {
        Self::new(
            path.span(),
            format!("#[validate({validation_type}(???))] needs validation path."),
        )
    }

    pub fn meta_path_custom_message_need_value(
        path: &syn::Path,
        custom_message_type: &str,
    ) -> Self {
        Self::new(
            path.span(),
            format!("#[validate(..., {custom_message_type}(???))] needs custom message path."),
        )
    }

    pub fn meta_list_validation_need_value(path: &syn::Path, validation_type: &str) -> Self {
        Self::new(
            path.span(),
            format!("#[validate({validation_type}(???, ...))] needs validation list."),
        )
    }

    pub fn meta_list_custom_message_need_value(
        path: &syn::Path,
        custom_message_type: &str,
    ) -> Self {
        Self::new(
            path.span(),
            format!("#[validate(..., {custom_message_type}(???, ...))] needs custom message list."),
        )
    }

    pub fn meta_name_value_validation_need_value(path: &syn::Path, validation_type: &str) -> Self {
        Self::new(
            path.span(),
            format!("#[validate({validation_type} = ???)] needs validation value."),
        )
    }

    pub fn meta_name_value_custom_message_need_value(
        path: &syn::Path,
        validation_type: &str,
    ) -> Self {
        Self::new(
            path.span(),
            format!("#[validate(..., {validation_type} = ???)] needs custom message value."),
        )
    }

    pub fn validate_attribute_parse_error(attribute: &syn::Attribute, error: &syn::Error) -> Self {
        Self::new(
            attribute.span(),
            format!("#[validate] parse error: {error}"),
        )
    }

    pub fn field_validation_type_required(attribute: &syn::Attribute) -> Self {
        let filterd_candidates: Vec<&str> = (MetaPathFieldValidation::iter().map(|x| x.name()))
            .chain(MetaListFieldValidation::iter().map(|x| x.name()))
            .chain(MetaNameValueFieldValidation::iter().map(|x| x.name()))
            .collect::<Vec<_>>();

        Self::new(
            attribute.meta.span(),
            format!("#[validate(???)] needs validation type. Is it one of the following?\n{filterd_candidates:#?}"),
        )
    }

    pub fn field_validation_type_unknown(path: &syn::Path, unknown: &str) -> Self {
        let candidates = &(MetaPathFieldValidation::iter().map(|x| x.name()))
            .chain(MetaListFieldValidation::iter().map(|x| x.name()))
            .chain(MetaNameValueFieldValidation::iter().map(|x| x.name()))
            .unique()
            .sorted()
            .collect::<Vec<_>>();

        let filterd_candidates =
            did_you_mean(unknown, candidates).unwrap_or_else(|| candidates.to_vec());

        Self::new(
            path.span(),
            format!("`{unknown}` is unknown validation type. Is it one of the following?\n{filterd_candidates:#?}"),
        )
    }

    pub fn struct_validation_type_required(attribute: &syn::Attribute) -> Self {
        let filterd_candidates: Vec<&str> = (MetaPathStructValidation::iter().map(|x| x.name()))
            .chain(MetaListStructValidation::iter().map(|x| x.name()))
            .chain(MetaNameValueStructValidation::iter().map(|x| x.name()))
            .collect::<Vec<_>>();

        Self::new(
            attribute.meta.span(),
            format!("#[validate(???)] needs validation type. Is it one of the following?\n{filterd_candidates:#?}"),
        )
    }

    pub fn struct_validation_type_unknown(path: &syn::Path, unknown: &str) -> Self {
        let candidates = &(MetaPathStructValidation::iter().map(|x| x.name()))
            .chain(MetaListStructValidation::iter().map(|x| x.name()))
            .chain(MetaNameValueStructValidation::iter().map(|x| x.name()))
            .collect::<Vec<_>>();

        let filterd_candidates =
            did_you_mean(unknown, candidates).unwrap_or_else(|| candidates.to_vec());

        Self::new(
            path.span(),
            format!("`{unknown}` is unknown validation type. Is it one of the following?\n{filterd_candidates:#?}"),
        )
    }

    pub fn unknown_custom_message_type(path: &syn::Path, unknown: &str) -> Self {
        let candidates = &(MetaPathCustomMessage::iter().map(|x| x.name()))
            .chain(MetaListCustomMessage::iter().map(|x| x.name()))
            .chain(MetaNameValueCustomMessage::iter().map(|x| x.name()))
            .unique()
            .sorted()
            .collect::<Vec<_>>();

        let filterd_candidates =
            did_you_mean(unknown, candidates).unwrap_or_else(|| candidates.to_vec());

        Self::new(
            path.span(),
            format!("`{unknown}` is unkown error message type. Is it one of the following?\n{filterd_candidates:#?}"),
        )
    }

    pub fn validate_enumerate_parse_error(meta_list: &syn::MetaList, error: &syn::Error) -> Self {
        Self::new(
            meta_list.span(),
            format!("#[validate(enumerate(???))] parse error: {error}"),
        )
    }

    pub fn validate_enumerate_need_item(path: &syn::Path) -> Self {
        Self::new(path.span(), "#[validate(enumerate(???))] needs items.")
    }

    pub fn validate_enumerate_need_array(path: impl Spanned) -> Self {
        Self::new(
            path.span(),
            "#[validate(enumerate = ???)] needs literal array only.",
        )
    }

    pub fn validate_custom_meta_list_need_function_or_closure(span: impl Spanned) -> Self {
        Self::new(
            span.span(),
            "#[validate(custom(???))] needs function or closure.",
        )
    }

    pub fn validate_custom_tail_error(nested: &crate::types::NestedMeta) -> Self {
        Self::new(
            nested.span(),
            "#[validate(custom(???))] supports only 1 item.",
        )
    }

    pub fn validate_custom_meta_name_value_need_function_or_closure(span: impl Spanned) -> Self {
        Self::new(
            span.span(),
            "#[validate(custom = ???)] needs function or closure.",
        )
    }

    pub fn custom_message_parse_error(ident: &syn::Ident, error: &syn::Error) -> Self {
        Self::new(
            ident.span(),
            format!("#[validate(..., {ident})] parse error: {error}"),
        )
    }

    pub fn message_fn_meta_list_need_item(path: &syn::Path) -> Self {
        Self::new(
            path.span(),
            "#[validate(..., message_fn(???))] needs function.",
        )
    }

    pub fn message_fn_meta_list_allow_name_path(nested_meta: &crate::types::NestedMeta) -> Self {
        Self::new(
            nested_meta.span(),
            "#[validate(..., message_fn(???))] allows only function name path.",
        )
    }

    pub fn message_fn_meta_list_tail_error(nested_meta: &crate::types::NestedMeta) -> Self {
        Self::new(
            nested_meta.span(),
            "#[validate(..., message_fn(???))] allows only 1 item.",
        )
    }

    pub fn message_fn_meta_name_value_needs_function_or_closure(
        meta_name_value: &syn::MetaNameValue,
    ) -> Self {
        Self::new(
            meta_name_value.span(),
            "#[validate(..., message_fn = ???)] needs function or closure.",
        )
    }

    #[cfg(feature = "fluent")]
    pub fn fluent_need_item(message_type: &MetaListCustomMessage, path: &syn::Path) -> Self {
        Self::new(
            path.span(),
            format!("`{}` needs items.", message_type.name()),
        )
    }

    #[cfg(feature = "fluent")]
    pub fn fluent_allow_key(
        message_type: &MetaListCustomMessage,
        nested_meta: &crate::types::NestedMeta,
    ) -> Self {
        Self::new(
            nested_meta.span(),
            format!(
                "#[validate(..., {}(???, ...))] allows only fluent key str",
                message_type.name()
            ),
        )
    }

    #[cfg(feature = "fluent")]
    pub fn fluent_allow_args(
        message_type: &MetaListCustomMessage,
        nested_meta: &crate::types::NestedMeta,
    ) -> Self {
        Self::new(
            nested_meta.span(),
            format!(
                "#[validate(..., {}(..., ???))] allows only fluent args key value.",
                message_type.name()
            ),
        )
    }

    #[cfg(feature = "fluent")]
    pub fn l10n_need_fn_call(expr: &syn::Expr) -> Self {
        Self::new(
            expr.span(),
            "#[validate(..., message_l10n = ???)] needs fn calling.".to_string(),
        )
    }

    #[cfg(feature = "fluent")]
    pub fn l10n_fn_name_not_allow(fn_name: &syn::Expr) -> Self {
        Self::new(
            fn_name.span(),
            "#[validate(..., message_l10n = ???(...))] allows only \"fluent\".".to_string(),
        )
    }

    #[cfg(feature = "fluent")]
    pub fn fluent_id_must_be_str_lit(expr: &syn::Expr) -> Self {
        Self::new(
            expr.span(),
            "#[validate(..., message_l10n = fluent(???, ...))] allow only string literal of the fluent id.",
        )
    }

    #[cfg(feature = "fluent")]
    pub fn fluent_id_not_found(paren: &syn::token::Paren) -> Self {
        Self::new(
            paren.span.span(),
            "#[validate(..., message_l10n = fluent(???))] need the fluent id.",
        )
    }

    #[cfg(feature = "fluent")]
    pub fn fluent_allow_arg(expr: &syn::Expr) -> Self {
        Self::new(
            expr.span(),
            "#[validate(..., message_l10n = fluent(..., ???))] allows only \"key=value\" of the fluent arg."
                .to_string(),
        )
    }

    pub fn literal_only(span: impl Spanned) -> Self {
        Self::new(span.span(), "Allow literal only.")
    }

    pub fn numeric_literal_only(lit: &syn::Lit) -> Self {
        Self::new(lit.span(), "Allow numeric literal only.")
    }

    pub fn str_literal_only(lit: &syn::Lit) -> Self {
        Self::new(lit.span(), "Allow str literal only.")
    }

    pub fn closure_not_supported(closure: &syn::ExprClosure) -> Self {
        Self::new(
            closure.or1_token.span(),
            format!("Closure not supported. {}", closure.to_token_stream()),
        )
    }

    pub fn too_many_list_items(nested_meta: &syn::Meta) -> Self {
        Self::new(nested_meta.span(), "Too many list items.")
    }

    pub fn to_compile_error(&self) -> TokenStream {
        self.0.to_compile_error()
    }

    pub fn validate_custom_does_not_support_custom_message(meta: &syn::Meta) -> Self {
        Self::new(
            meta.span(),
            "#[validate(custon(...), ???)] does not support custom error message.",
        )
    }
}

fn did_you_mean<'a, T, I>(unknown: &'a str, candidates: I) -> Option<Vec<&'a str>>
where
    T: AsRef<str> + 'a,
    I: IntoIterator<Item = &'a T>,
{
    let mut filterd = candidates
        .into_iter()
        .map(|candidate| {
            (
                ::strsim::jaro_winkler(unknown, candidate.as_ref()),
                candidate.as_ref(),
            )
        })
        .filter(|(confidence, _)| *confidence > 0.8)
        .collect::<Vec<_>>();

    if filterd.is_empty() {
        None
    } else {
        filterd.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
        Some(
            filterd
                .into_iter()
                .map(|(_, candidate)| candidate)
                .collect(),
        )
    }
}

pub type Errors = Vec<Error>;

pub fn to_compile_errors(errors: Errors) -> TokenStream {
    let compile_errors = errors.iter().map(Error::to_compile_error);
    quote!(#(#compile_errors)*)
}