serde_valid_derive 0.16.0-alpha

JSON Schema based validation tool using with serde.
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;

use crate::types::CommaSeparatedNestedMetas;
#[cfg(feature = "fluent")]
use crate::validate::MetaListMessage;

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::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::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::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().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()))
    }

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

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

    pub fn rule_need_function(path: &syn::Path) -> Self {
        Self::new(path.span(), "#[rule(???)] needs rule_fn.")
    }

    pub fn rule_allow_single_function(nested_meta: &syn::NestedMeta) -> Self {
        Self::new(nested_meta.span(), "#[rule] allow single function.")
    }

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

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

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

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

    pub fn validate_meta_literal_not_support(lit: &syn::Lit) -> Self {
        Self::new(lit.span(), "#[validate(???)] does not support literal.")
    }

    pub fn validate_meta_name_value_not_support(name_value: syn::MetaNameValue) -> Self {
        Self::new(
            name_value.span(),
            "#[validate = ???] format does not support.",
        )
    }

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

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

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

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

    pub fn validate_type_required_error(attribute: &syn::Attribute) -> Self {
        Self::new(attribute.span(), "#[validate(???)] needs validation type.")
    }

    pub fn validate_unknown_type(path: &syn::Path, unknown: &str, candidates: &[&str]) -> Self {
        let filterd_candidates =
            did_you_mean(unknown, candidates).unwrap_or_else(|| candidates.to_vec());

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

    pub fn validate_enumerate_need_item(path: &syn::Path) -> Self {
        Self::new(path.span(), "`enumerate` need items.")
    }

    pub fn validate_custom_need_item(path: &syn::Path) -> Self {
        Self::new(path.span(), "`custom` need items.")
    }

    pub fn validate_custom_tail_error(nested: &CommaSeparatedNestedMetas) -> Self {
        Self::new(nested.span(), "`custom` support only 1 item.")
    }

    pub fn message_fn_need_item(path: &syn::Path) -> Self {
        Self::new(path.span(), "`message_fn` need items.")
    }

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

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

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

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

    pub fn message_fn_tail_error(nested_meta: &syn::NestedMeta) -> Self {
        Self::new(nested_meta.span(), "`message_fn` support only 1 item.")
    }

    pub fn literal_only(meta: &syn::Meta) -> Self {
        Self::new(meta.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 literal_not_support(lit: &syn::Lit) -> Self {
        Self::new(lit.span(), "Literal does not support.")
    }

    pub fn meta_name_value_not_support(name_value: &syn::MetaNameValue) -> Self {
        Self::new(name_value.span(), "Name value does not support.")
    }

    pub fn meta_path_not_support(path: &syn::Path) -> Self {
        Self::new(path.span(), "Path does not support.")
    }

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

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

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)*)
}