serde_valid_derive 2.0.2

JSON Schema based validation tool using serde.
Documentation
mod meta_list;
mod meta_name_value;
mod meta_path;

use crate::{
    attribute::{
        common::message_format::{default_message_format, extract_custom_message_format},
        MetaListStructValidation, MetaNameValueStructValidation, MetaPathStructValidation,
        Validator,
    },
    types::SingleIdentPath,
    warning::WithWarnings,
};
use quote::quote;
use std::str::FromStr;

use self::{
    meta_list::extract_variant_validator_from_meta_list,
    meta_name_value::extract_variant_validator_from_meta_name_value,
    meta_path::extract_variant_validator_from_meta_path,
};

pub fn extract_variant_validator(
    attribute: &syn::Attribute,
) -> Result<WithWarnings<Validator>, crate::Errors> {
    match &attribute.meta {
        syn::Meta::Path(_) => Ok(WithWarnings::new(quote!())),
        syn::Meta::List(list) => inner_extract_variant_validator(attribute, list),
        syn::Meta::NameValue(name_value) => {
            Err(vec![crate::Error::validate_meta_name_value_not_supported(
                name_value,
            )])
        }
    }
}

fn inner_extract_variant_validator(
    attribute: &syn::Attribute,
    meta_list: &syn::MetaList,
) -> Result<WithWarnings<Validator>, crate::Errors> {
    let mut errors = vec![];
    let nested = meta_list
        .parse_args_with(crate::types::CommaSeparatedMetas::parse_terminated)
        .map_err(|error| {
            vec![crate::Error::validate_attribute_parse_error(
                attribute, &error,
            )]
        })?;

    let WithWarnings {
        data: message_format,
        mut warnings,
    } = match nested.len() {
        0 => Err(vec![crate::Error::struct_validation_type_required(
            attribute,
        )])?,
        1 => None,
        2 => match extract_custom_message_format(&nested[1]) {
            Ok(custom_message) => {
                if nested[0].path().is_ident("custom") {
                    errors.push(
                        crate::Error::validate_custom_does_not_support_custom_message(&nested[1]),
                    );
                    None
                } else {
                    Some(custom_message)
                }
            }
            Err(message_fn_errors) => {
                errors.extend(message_fn_errors);
                None
            }
        },
        _ => {
            for meta in nested.iter().skip(2) {
                errors.push(crate::Error::too_many_list_items(meta));
            }
            None
        }
    }
    .unwrap_or_else(|| WithWarnings::new(default_message_format()));

    let meta = &nested[0];
    let validation_path = match meta {
        syn::Meta::Path(path) => path,
        syn::Meta::List(list) => &list.path,
        syn::Meta::NameValue(name_value) => &name_value.path,
    };

    let validation_name = SingleIdentPath::new(validation_path)
        .map_err(|error| vec![error])?
        .ident()
        .to_string();
    let validator = match (
        MetaPathStructValidation::from_str(&validation_name),
        MetaListStructValidation::from_str(&validation_name),
        MetaNameValueStructValidation::from_str(&validation_name),
        meta,
    ) {
        (Ok(validation_type), _, _, syn::Meta::Path(validation)) => {
            extract_variant_validator_from_meta_path(validation_type, validation, message_format)
        }

        (_, Ok(validation_type), _, syn::Meta::List(validation)) => {
            extract_variant_validator_from_meta_list(validation_type, validation, message_format)
        }

        (_, _, Ok(validation_type), syn::Meta::NameValue(validation)) => {
            extract_variant_validator_from_meta_name_value(
                validation_type,
                validation,
                message_format,
            )
        }

        (Ok(_), _, _, _) => Err(vec![crate::Error::meta_path_validation_need_value(
            validation_path,
            &validation_name,
        )]),

        (_, Ok(_), _, _) => Err(vec![crate::Error::meta_list_validation_need_value(
            validation_path,
            &validation_name,
        )]),

        (_, _, Ok(_), _) => Err(vec![crate::Error::meta_name_value_validation_need_value(
            validation_path,
            &validation_name,
        )]),

        _ => Err(vec![crate::Error::struct_validation_type_unknown(
            validation_path,
            &validation_name,
        )]),
    }
    .map(|validator| {
        warnings.extend(validator.warnings);
        validator.data
    });

    match validator {
        Ok(validator) => {
            if errors.is_empty() {
                Ok(WithWarnings {
                    data: validator,
                    warnings,
                })
            } else {
                Err(errors)
            }
        }
        Err(validator_errors) => {
            errors.extend(validator_errors);
            Err(errors)
        }
    }
}