From f06d7f595cfaea51307e1f7534fd85902c27be09 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Mon, 8 Jan 2024 15:24:18 +0900 Subject: [PATCH] feat: add struct custom validator. --- serde_valid/src/lib.rs | 9 + serde_valid/tests/struct_custom_test.rs | 82 +++++++ .../src/derive/named_struct_derive.rs | 18 +- .../src/derive/unnamed_struct_derive.rs | 19 +- serde_valid_derive/src/error.rs | 81 ++++--- serde_valid_derive/src/rule.rs | 2 + .../src/rule/named_struct_rule.rs | 16 +- serde_valid_derive/src/rule/struct_custom.rs | 221 ++++++++++++++++++ .../src/rule/unnamed_struct_rule.rs | 16 +- serde_valid_derive/src/validate.rs | 7 +- serde_valid_derive/src/validate/common.rs | 26 ++- .../src/validate/generic/custom.rs | 21 +- serde_valid_derive/src/validate/meta.rs | 10 +- .../src/validate/meta/meta_list.rs | 154 ++++++------ .../src/validate/meta/meta_path.rs | 14 +- .../src/validate/meta/nested_meta_list.rs | 10 +- .../validate/meta/nested_meta_name_value.rs | 54 +++-- .../src/validate/meta/nested_meta_path.rs | 8 +- 18 files changed, 569 insertions(+), 199 deletions(-) create mode 100644 serde_valid/tests/struct_custom_test.rs create mode 100644 serde_valid_derive/src/rule/struct_custom.rs diff --git a/serde_valid/src/lib.rs b/serde_valid/src/lib.rs index c651a6e4..4a379e53 100644 --- a/serde_valid/src/lib.rs +++ b/serde_valid/src/lib.rs @@ -593,3 +593,12 @@ where } pub use serde_valid_derive::Validate; + +pub mod helpers { + pub fn wrap_closure_validation( + data: &T, + f: impl FnOnce(&T) -> Result<(), crate::validation::Error>, + ) -> Result<(), crate::validation::Error> { + f(data) + } +} diff --git a/serde_valid/tests/struct_custom_test.rs b/serde_valid/tests/struct_custom_test.rs new file mode 100644 index 00000000..e99ff29c --- /dev/null +++ b/serde_valid/tests/struct_custom_test.rs @@ -0,0 +1,82 @@ +use serde_valid::Validate; + +#[test] +fn named_struct_custom_is_ok() { + fn sample_struct_validation(_val: &TestStruct) -> Result<(), serde_valid::validation::Error> { + Ok(()) + } + + #[derive(Validate)] + #[validate(custom(sample_struct_validation))] + struct TestStruct { + val: i32, + } + + let s = TestStruct { val: 5 }; + assert_eq!(s.val, 5); + assert!(s.validate().is_ok()); +} + +#[test] +fn named_struct_custom_closure_is_ok() { + fn sample_struct_validation(_val: i32) -> Result<(), serde_valid::validation::Error> { + Ok(()) + } + + #[derive(Validate)] + #[validate(custom(|s| sample_struct_validation(s.val)))] + struct TestStruct { + val: i32, + } + + let s = TestStruct { val: 5 }; + assert_eq!(s.val, 5); + assert!(s.validate().is_ok()); +} + +#[test] +fn unnamed_struct_custom_is_ok() { + fn sample_struct_validation(_val: &TestStruct) -> Result<(), serde_valid::validation::Error> { + Ok(()) + } + + #[derive(Validate)] + #[validate(custom(sample_struct_validation))] + struct TestStruct(i32); + + let s = TestStruct(5); + assert_eq!(s.0, 5); + assert!(s.validate().is_ok()); +} + +#[test] +fn unnamed_struct_custom_closure_is_ok() { + fn sample_struct_validation(_val: i32) -> Result<(), serde_valid::validation::Error> { + Ok(()) + } + + #[derive(Validate)] + #[validate(custom(|s| sample_struct_validation(s.0)))] + struct TestStruct(i32); + + let s = TestStruct(5); + assert_eq!(s.0, 5); + assert!(s.validate().is_ok()); +} + +#[test] +fn unnamed_struct_custom_closure_is_err() { + fn sample_struct_validation(_val: i32) -> Result<(), serde_valid::validation::Error> { + Err(serde_valid::validation::Error::Custom( + "Struct Validation Error.".to_owned(), + )) + } + + #[derive(Validate)] + #[validate(custom(|s| sample_struct_validation(s.0)))] + struct TestStruct(i32); + + let s = TestStruct(5); + assert_eq!(s.0, 5); + assert!(s.validate().is_err()); +} diff --git a/serde_valid_derive/src/derive/named_struct_derive.rs b/serde_valid_derive/src/derive/named_struct_derive.rs index 6d328139..ec398dc0 100644 --- a/serde_valid_derive/src/derive/named_struct_derive.rs +++ b/serde_valid_derive/src/derive/named_struct_derive.rs @@ -1,8 +1,8 @@ use crate::error::object_errors_tokens; -use crate::rule::collect_rules_from_named_struct; +use crate::rule::{collect_rules_from_named_struct, collect_struct_custom_from_named_struct}; use crate::serde::rename::{collect_serde_rename_map, RenameMap}; use crate::types::{Field, NamedField}; -use crate::validate::{extract_meta_validator, FieldValidators}; +use crate::validate::{extract_field_validator, FieldValidators}; use proc_macro2::TokenStream; use quote::quote; use std::borrow::Cow; @@ -26,8 +26,15 @@ pub fn expand_named_struct_derive( (HashSet::new(), quote!()) } }; + let struct_validations = match collect_struct_custom_from_named_struct(&input.attrs) { + Ok(validations) => TokenStream::from_iter(validations), + Err(rule_errors) => { + errors.extend(rule_errors); + quote!() + } + }; - let validates = match collect_named_fields_validators_list(fields, &rename_map) { + let field_validates = match collect_named_fields_validators_list(fields, &rename_map) { Ok(field_validators) => TokenStream::from_iter(field_validators.iter().map(|validator| { if validator.is_empty() && rule_fields.contains(validator.ident()) { validator.get_field_variable_token() @@ -50,7 +57,8 @@ pub fn expand_named_struct_derive( let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); - #validates + #field_validates + #struct_validations #rules if __rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty() { @@ -105,7 +113,7 @@ fn collect_named_field_validators<'a>( .iter() .filter_map(|attribute| { if attribute.path().is_ident("validate") { - match extract_meta_validator(&named_field, attribute, rename_map) { + match extract_field_validator(&named_field, attribute, rename_map) { Ok(validator) => Some(validator), Err(validator_error) => { errors.extend(validator_error); diff --git a/serde_valid_derive/src/derive/unnamed_struct_derive.rs b/serde_valid_derive/src/derive/unnamed_struct_derive.rs index 33d68fd7..85602f61 100644 --- a/serde_valid_derive/src/derive/unnamed_struct_derive.rs +++ b/serde_valid_derive/src/derive/unnamed_struct_derive.rs @@ -1,7 +1,7 @@ use crate::error::{array_errors_tokens, new_type_errors_tokens}; -use crate::rule::collect_rules_from_unnamed_struct; +use crate::rule::{collect_rules_from_unnamed_struct, collect_struct_custom_from_named_struct}; use crate::types::{Field, UnnamedField}; -use crate::validate::{extract_meta_validator, FieldValidators}; +use crate::validate::{extract_field_validator, FieldValidators}; use proc_macro2::TokenStream; use quote::quote; use std::borrow::Cow; @@ -25,7 +25,15 @@ pub fn expand_unnamed_struct_derive( } }; - let validates = match collect_unnamed_fields_validators_list(fields) { + let struct_validations = match collect_struct_custom_from_named_struct(&input.attrs) { + Ok(validations) => TokenStream::from_iter(validations), + Err(rule_errors) => { + errors.extend(rule_errors); + quote!() + } + }; + + let field_validates = match collect_unnamed_fields_validators_list(fields) { Ok(field_validators) => TokenStream::from_iter(field_validators.iter().map(|validator| { if validator.is_empty() && rule_fields.contains(validator.ident()) { validator.get_field_variable_token() @@ -52,7 +60,8 @@ pub fn expand_unnamed_struct_derive( let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); - #validates + #field_validates + #struct_validations #rules if __rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty() { @@ -105,7 +114,7 @@ fn collect_unnamed_field_validators( .iter() .filter_map(|attribute| { if attribute.path().is_ident("validate") { - match extract_meta_validator(&unnamed_field, attribute, &HashMap::new()) { + match extract_field_validator(&unnamed_field, attribute, &HashMap::new()) { Ok(validator) => Some(validator), Err(validator_errors) => { errors.extend(validator_errors); diff --git a/serde_valid_derive/src/error.rs b/serde_valid_derive/src/error.rs index 6c160800..371359f4 100644 --- a/serde_valid_derive/src/error.rs +++ b/serde_valid_derive/src/error.rs @@ -1,6 +1,7 @@ use crate::validate::{ - MetaListValidation, MetaNameValueCustomMessage, MetaNameValueValidation, MetaPathCustomMessage, - MetaPathValidation, + MetaListFieldValidation, MetaListStructValidation, MetaNameValueCustomMessage, + MetaNameValueFieldValidation, MetaNameValueStructValidation, MetaPathCustomMessage, + MetaPathFieldValidation, MetaPathStructValidation, }; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; @@ -140,7 +141,12 @@ 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()) + .chain( + __item_vec_errors_map + .remove(&0) + .unwrap_or(vec![]) + .into_iter() + ) .collect() )) } @@ -181,9 +187,9 @@ impl Error { Self::new(path.span(), "`rule` function needs arguments.") } - pub fn rule_args_parse_error(metalist: &syn::MetaList, error: &syn::Error) -> Self { + pub fn rule_args_parse_error(meta_list: &syn::MetaList, error: &syn::Error) -> Self { Self::new( - metalist.span(), + meta_list.span(), format!("#[rule(???)] parse error: {error}"), ) } @@ -222,10 +228,7 @@ impl Error { } pub fn validate_meta_name_value_not_support(name_value: &syn::MetaNameValue) -> Self { - Self::new( - name_value.span(), - "#[validate = ???] format does not support.", - ) + Self::new(name_value.span(), "#[validate = ???] does not support.") } pub fn meta_path_validation_need_value(path: &syn::Path, validation_type: &str) -> Self { @@ -286,10 +289,37 @@ impl Error { ) } - pub fn validate_type_required_error(attribute: &syn::Attribute) -> Self { - let filterd_candidates: Vec<&str> = (MetaPathValidation::iter().map(|x| x.name())) - .chain(MetaNameValueValidation::iter().map(|x| x.name())) - .chain(MetaListValidation::iter().map(|x| x.name())) + 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::>(); + + 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())) + .collect::>(); + + 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::>(); Self::new( @@ -298,10 +328,10 @@ impl Error { ) } - pub fn unknown_validation_type(path: &syn::Path, unknown: &str) -> Self { - let candidates = &(MetaPathValidation::iter().map(|x| x.name())) - .chain(MetaListValidation::iter().map(|x| x.name())) - .chain(MetaNameValueValidation::iter().map(|x| x.name())) + 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::>(); let filterd_candidates = @@ -328,9 +358,9 @@ impl Error { ) } - pub fn validate_enumerate_parse_error(metalist: &syn::MetaList, error: &syn::Error) -> Self { + pub fn validate_enumerate_parse_error(meta_list: &syn::MetaList, error: &syn::Error) -> Self { Self::new( - metalist.span(), + meta_list.span(), format!("#[validate(enumerate(???))] parse error: {error}"), ) } @@ -339,14 +369,17 @@ impl Error { Self::new(path.span(), "#[validate(enumerate(???))] needs items.") } - pub fn validate_custom_need_function(span: impl Spanned) -> Self { - Self::new(span.span(), "#[validate(custom(???))] needs function.") + pub fn validate_custom_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.", + "#[validate(custom(???))] supports only 1 item.", ) } @@ -426,10 +459,6 @@ impl Error { Self::new(lit.span(), "Allow str literal only.") } - pub fn literal_not_support(lit: &syn::Lit) -> Self { - Self::new(lit.span(), "Literal not support.") - } - pub fn closure_not_support(closure: &syn::ExprClosure) -> Self { Self::new( closure.or1_token.span(), diff --git a/serde_valid_derive/src/rule.rs b/serde_valid_derive/src/rule.rs index 3783fb79..c4f8541f 100644 --- a/serde_valid_derive/src/rule.rs +++ b/serde_valid_derive/src/rule.rs @@ -1,5 +1,7 @@ mod named_struct_rule; +mod struct_custom; mod unnamed_struct_rule; pub use named_struct_rule::collect_rules_from_named_struct; +pub use struct_custom::collect_struct_custom_from_named_struct; pub use unnamed_struct_rule::collect_rules_from_unnamed_struct; diff --git a/serde_valid_derive/src/rule/named_struct_rule.rs b/serde_valid_derive/src/rule/named_struct_rule.rs index 8fe812c8..493d0ee7 100644 --- a/serde_valid_derive/src/rule/named_struct_rule.rs +++ b/serde_valid_derive/src/rule/named_struct_rule.rs @@ -43,17 +43,17 @@ pub fn collect_rules_from_named_struct( } fn collect_rule( - metalist: &syn::MetaList, + meta_list: &syn::MetaList, ) -> Result<(HashSet, TokenStream), crate::Errors> { let mut errors = vec![]; - let nested = metalist + let nested = meta_list .parse_args_with(CommaSeparatedNestedMetas::parse_terminated) - .map_err(|error| vec![crate::Error::rule_args_parse_error(metalist, &error)])?; + .map_err(|error| vec![crate::Error::rule_args_parse_error(meta_list, &error)])?; match nested.len() { 0 => Err(vec![crate::Error::rule_allow_function_call_or_closure( - metalist, + meta_list, )])?, 2.. => nested .iter() @@ -83,14 +83,14 @@ fn collect_rule( } fn extract_rule_from_meta_list( - metalist: &syn::MetaList, + meta_list: &syn::MetaList, ) -> Result<(HashSet, TokenStream), crate::Errors> { let mut errors = vec![]; - let rule_fn_name = &metalist.path; - let nested = metalist + let rule_fn_name = &meta_list.path; + let nested = meta_list .parse_args_with(crate::types::CommaSeparatedNestedMetas::parse_terminated) - .map_err(|error| vec![crate::Error::rule_args_parse_error(metalist, &error)])?; + .map_err(|error| vec![crate::Error::rule_args_parse_error(meta_list, &error)])?; if nested.is_empty() { errors.push(crate::Error::rule_need_arguments(rule_fn_name)); diff --git a/serde_valid_derive/src/rule/struct_custom.rs b/serde_valid_derive/src/rule/struct_custom.rs new file mode 100644 index 00000000..769f7903 --- /dev/null +++ b/serde_valid_derive/src/rule/struct_custom.rs @@ -0,0 +1,221 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use crate::types::{CommaSeparatedNestedMetas, SingleIdentPath}; +use crate::validate::{ + MetaListStructValidation, MetaNameValueStructValidation, MetaPathStructValidation, Validator, +}; +use std::str::FromStr; + +pub fn collect_struct_custom_from_named_struct( + attributes: &[syn::Attribute], +) -> Result { + let mut errors = vec![]; + + let validations = attributes + .iter() + .filter_map(|attribute| { + if attribute.path().is_ident("validate") { + match extract_struct_validator(attribute) { + Ok(validator) => Some(validator), + Err(validator_error) => { + errors.extend(validator_error); + None + } + } + } else { + None + } + }) + .collect::>(); + + if errors.is_empty() { + Ok(TokenStream::from_iter(validations)) + } else { + Err(errors) + } +} + +fn extract_struct_validator(attribute: &syn::Attribute) -> Result { + match &attribute.meta { + syn::Meta::Path(_) => Ok(quote!()), + syn::Meta::List(list) => extract_struct_validator_from_meta_list(attribute, list), + syn::Meta::NameValue(name_value) => { + Err(vec![crate::Error::validate_meta_name_value_not_support( + name_value, + )]) + } + } +} + +fn extract_struct_validator_from_meta_list( + attribute: &syn::Attribute, + meta_list: &syn::MetaList, +) -> Result { + 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, + )] + })?; + + match nested.len() { + 0 => Err(vec![crate::Error::struct_validation_type_required( + attribute, + )])?, + 1 => {} + _ => { + for meta in nested.iter().skip(2) { + errors.push(crate::Error::too_many_list_items(meta)); + } + } + }; + + 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).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_struct_validator_from_nested_meta_path(validation_type, validation) + } + + (_, Ok(validation_type), _, syn::Meta::List(validation)) => { + extract_struct_validator_from_nested_meta_list(validation_type, validation) + } + + (_, _, Ok(validation_type), syn::Meta::NameValue(validation)) => { + extract_struct_validator_from_nested_meta_name_value(validation_type, validation) + } + + (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, + )]), + }; + + match validator { + Ok(validator) => { + if errors.is_empty() { + Ok(validator) + } else { + Err(errors) + } + } + Err(validator_errors) => { + errors.extend(validator_errors); + Err(errors) + } + } +} + +#[inline] +fn extract_struct_validator_from_nested_meta_path( + validation_type: MetaPathStructValidation, + _validation: &syn::Path, +) -> Result { + match validation_type {} +} + +fn extract_struct_validator_from_nested_meta_list( + validation_type: MetaListStructValidation, + validation: &syn::MetaList, +) -> Result { + match validation_type { + MetaListStructValidation::Custom => extract_struct_custom_validator(validation), + } +} + +#[inline] +fn extract_struct_validator_from_nested_meta_name_value( + validation_type: MetaNameValueStructValidation, + _validation: &syn::MetaNameValue, +) -> Result { + match validation_type {} +} + +fn extract_struct_custom_validator(meta_path: &syn::MetaList) -> Result { + let mut errors = vec![]; + + let nested = meta_path + .parse_args_with(CommaSeparatedNestedMetas::parse_terminated) + .map_err(|error| vec![crate::Error::rule_args_parse_error(meta_path, &error)])?; + + match nested.len() { + 0 => Err(vec![ + crate::Error::validate_custom_need_function_or_closure(meta_path), + ])?, + 2.. => nested + .iter() + .skip(1) + .for_each(|error| errors.push(crate::Error::rule_allow_single_function(error))), + _ => {} + } + + let rule = match &nested[0] { + crate::types::NestedMeta::Meta(syn::Meta::Path(path)) => { + extract_struct_custom_from_meta_path(path) + } + crate::types::NestedMeta::Closure(closure) => extract_struct_custom_from_closure(closure), + _ => Err(vec![ + crate::Error::validate_custom_need_function_or_closure(&nested[0]), + ]), + }; + + match rule { + Ok(_) => { + if errors.is_empty() { + rule + } else { + Err(errors) + } + } + Err(rule_errors) => Err(errors.into_iter().chain(rule_errors).collect()), + } +} + +fn extract_struct_custom_from_meta_path(meta_path: &syn::Path) -> Result { + let rule_fn_name = &meta_path; + + Ok(quote!( + if let Err(__error) = #rule_fn_name(self) { + __rule_vec_errors.push(__error); + }; + )) +} + +fn extract_struct_custom_from_closure( + closure: &syn::ExprClosure, +) -> Result { + Ok(quote!( + if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #closure) { + __rule_vec_errors.push(__error); + }; + )) +} diff --git a/serde_valid_derive/src/rule/unnamed_struct_rule.rs b/serde_valid_derive/src/rule/unnamed_struct_rule.rs index 2dea3901..80e603ee 100644 --- a/serde_valid_derive/src/rule/unnamed_struct_rule.rs +++ b/serde_valid_derive/src/rule/unnamed_struct_rule.rs @@ -43,17 +43,17 @@ pub fn collect_rules_from_unnamed_struct( } fn collect_rule( - metalist: &syn::MetaList, + meta_list: &syn::MetaList, ) -> Result<(HashSet, TokenStream), crate::Errors> { let mut errors = vec![]; - let nested = metalist + let nested = meta_list .parse_args_with(CommaSeparatedNestedMetas::parse_terminated) - .map_err(|error| vec![crate::Error::rule_args_parse_error(metalist, &error)])?; + .map_err(|error| vec![crate::Error::rule_args_parse_error(meta_list, &error)])?; match nested.len() { 0 => Err(vec![crate::Error::rule_allow_function_call_or_closure( - metalist.path.span(), + meta_list.path.span(), )])?, 2.. => nested.iter().skip(1).for_each(|nested_meta| { errors.push(crate::Error::rule_allow_single_function(nested_meta)) @@ -83,14 +83,14 @@ fn collect_rule( } fn extract_rule_from_meta_list( - metalist: &syn::MetaList, + meta_list: &syn::MetaList, ) -> Result<(HashSet, TokenStream), crate::Errors> { let mut errors = vec![]; - let rule_fn_name = &metalist.path; - let nested = metalist + let rule_fn_name = &meta_list.path; + let nested = meta_list .parse_args_with(CommaSeparatedNestedMetas::parse_terminated) - .map_err(|error| vec![crate::Error::rule_args_parse_error(metalist, &error)])?; + .map_err(|error| vec![crate::Error::rule_args_parse_error(meta_list, &error)])?; if nested.is_empty() { errors.push(crate::Error::rule_need_arguments(rule_fn_name)); } diff --git a/serde_valid_derive/src/validate.rs b/serde_valid_derive/src/validate.rs index 3612c880..33a9403a 100644 --- a/serde_valid_derive/src/validate.rs +++ b/serde_valid_derive/src/validate.rs @@ -8,9 +8,10 @@ mod object; mod string; pub use common::{ - MetaListCustomMessage, MetaListValidation, MetaNameValueCustomMessage, MetaNameValueValidation, - MetaPathCustomMessage, MetaPathValidation, + MetaListCustomMessage, MetaListFieldValidation, MetaListStructValidation, + MetaNameValueCustomMessage, MetaNameValueFieldValidation, MetaNameValueStructValidation, + MetaPathCustomMessage, MetaPathFieldValidation, MetaPathStructValidation, }; pub use field::{FieldValidators, Validator}; -pub use meta::extract_meta_validator; +pub use meta::extract_field_validator; diff --git a/serde_valid_derive/src/validate/common.rs b/serde_valid_derive/src/validate/common.rs index ca116315..796d5b5e 100644 --- a/serde_valid_derive/src/validate/common.rs +++ b/serde_valid_derive/src/validate/common.rs @@ -15,10 +15,12 @@ macro_rules! enum_str { } impl $name { + #[allow(dead_code)] pub fn name(&self) -> &'static str { unimplemented!() } + #[allow(dead_code)] pub fn iter() -> std::array::IntoIter { [].into_iter() } @@ -41,12 +43,14 @@ macro_rules! enum_str { } impl $name { + #[allow(dead_code)] pub fn name(&self) -> &'static str { match *self { $($name::$variant => $val),* } } + #[allow(dead_code)] pub fn iter() -> std::array::IntoIter { [ $($name::$variant),* @@ -68,20 +72,36 @@ macro_rules! enum_str { } enum_str! { - pub enum MetaPathValidation { + pub enum MetaPathStructValidation { + } +} + +enum_str! { + pub enum MetaListStructValidation { + Custom = "custom", + } +} + +enum_str! { + pub enum MetaNameValueStructValidation { + } +} + +enum_str! { + pub enum MetaPathFieldValidation { UniqueItems = "unique_items", } } enum_str! { - pub enum MetaListValidation { + pub enum MetaListFieldValidation { Enumerate = "enumerate", Custom = "custom", } } enum_str! { - pub enum MetaNameValueValidation { + pub enum MetaNameValueFieldValidation { Minimum = "minimum", Maximum = "maximum", ExclusiveMinimum = "exclusive_minimum", diff --git a/serde_valid_derive/src/validate/generic/custom.rs b/serde_valid_derive/src/validate/generic/custom.rs index 19e93c8e..4048759e 100644 --- a/serde_valid_derive/src/validate/generic/custom.rs +++ b/serde_valid_derive/src/validate/generic/custom.rs @@ -21,7 +21,9 @@ pub fn extract_generic_custom_validator( .map_err(|error| vec![crate::Error::custom_message_parse_error(path_ident, &error)])?; let custom_fn_name = match nested.len() { - 0 => Err(vec![crate::Error::validate_custom_need_function(path)]), + 0 => Err(vec![ + crate::Error::validate_custom_need_function_or_closure(path), + ]), 1 => extract_custom_fn_name(&nested[0]), _ => Err(nested .iter() @@ -44,19 +46,10 @@ fn extract_custom_fn_name( nested_meta: &crate::types::NestedMeta, ) -> Result { match nested_meta { - crate::types::NestedMeta::Meta(meta) => match meta { - syn::Meta::List(list) => { - let fn_name = &list.path; - Ok(quote!(#fn_name)) - } - syn::Meta::NameValue(name_value) => { - Err(vec![crate::Error::validate_custom_need_function( - name_value, - )]) - } - syn::Meta::Path(fn_name) => Ok(quote!(#fn_name)), - }, - crate::types::NestedMeta::Lit(lit) => Err(vec![crate::Error::literal_not_support(lit)]), + crate::types::NestedMeta::Meta(syn::Meta::Path(fn_name)) => Ok(quote!(#fn_name)), crate::types::NestedMeta::Closure(closure) => Ok(quote!((#closure))), + _ => Err(vec![ + crate::Error::validate_custom_need_function_or_closure(nested_meta), + ]), } } diff --git a/serde_valid_derive/src/validate/meta.rs b/serde_valid_derive/src/validate/meta.rs index e4b049f7..00bc86eb 100644 --- a/serde_valid_derive/src/validate/meta.rs +++ b/serde_valid_derive/src/validate/meta.rs @@ -7,20 +7,20 @@ mod nested_meta_path; use crate::serde::rename::RenameMap; use crate::types::Field; use crate::validate::Validator; -use meta_path::extract_validator_from_meta_path; +use meta_path::extract_field_validator_from_meta_path; -use self::meta_list::extract_validator_from_meta_list; +use self::meta_list::extract_field_validator_from_meta_list; -pub fn extract_meta_validator( +pub fn extract_field_validator( field: &impl Field, attribute: &syn::Attribute, rename_map: &RenameMap, ) -> Result { match &attribute.meta { syn::Meta::List(list) => { - extract_validator_from_meta_list(field, attribute, list, rename_map) + extract_field_validator_from_meta_list(field, attribute, list, rename_map) } - syn::Meta::Path(_) => extract_validator_from_meta_path(field, rename_map), + syn::Meta::Path(_) => extract_field_validator_from_meta_path(field, rename_map), syn::Meta::NameValue(name_value) => { Err(vec![crate::Error::validate_meta_name_value_not_support( name_value, diff --git a/serde_valid_derive/src/validate/meta/meta_list.rs b/serde_valid_derive/src/validate/meta/meta_list.rs index a23d716a..823ed2f4 100644 --- a/serde_valid_derive/src/validate/meta/meta_list.rs +++ b/serde_valid_derive/src/validate/meta/meta_list.rs @@ -1,13 +1,15 @@ -use super::nested_meta_list::extract_validator_from_nested_meta_list; -use super::nested_meta_name_value::extract_validator_from_nested_meta_name_value; -use super::nested_meta_path::extract_validator_from_nested_meta_path; +use super::nested_meta_list::extract_field_validator_from_nested_meta_list; +use super::nested_meta_name_value::extract_field_validator_from_nested_meta_name_value; +use super::nested_meta_path::extract_field_validator_from_nested_meta_path; use crate::serde::rename::RenameMap; use crate::types::{Field, SingleIdentPath}; use crate::validate::common::{extract_custom_message_tokens, CustomMessageToken}; -use crate::validate::{MetaListValidation, MetaNameValueValidation, MetaPathValidation, Validator}; +use crate::validate::{ + MetaListFieldValidation, MetaNameValueFieldValidation, MetaPathFieldValidation, Validator, +}; use std::str::FromStr; -pub fn extract_validator_from_meta_list( +pub fn extract_field_validator_from_meta_list( field: &impl Field, attribute: &syn::Attribute, meta_list: &syn::MetaList, @@ -23,7 +25,10 @@ pub fn extract_validator_from_meta_list( })?; let custom_message = match nested.len() { - 0..=1 => CustomMessageToken::default(), + 0 => Err(vec![crate::Error::field_validation_type_required( + attribute, + )])?, + 1 => CustomMessageToken::default(), 2 => match extract_custom_message_tokens(&nested[1]) { Ok(custom_message) => custom_message, Err(message_fn_errors) => { @@ -39,89 +44,84 @@ pub fn extract_validator_from_meta_list( } }; - if !nested.is_empty() { - let meta = &nested[0]; + 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_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).ident().to_string(); + let validation_name = SingleIdentPath::new(validation_path).ident().to_string(); - let validator = match ( - MetaPathValidation::from_str(&validation_name), - MetaListValidation::from_str(&validation_name), - MetaNameValueValidation::from_str(&validation_name), - meta, - ) { - (Ok(validation_type), _, _, syn::Meta::Path(validation)) => { - extract_validator_from_nested_meta_path( - field, - validation_type, - validation, - custom_message, - rename_map, - ) - } + let validator = match ( + MetaPathFieldValidation::from_str(&validation_name), + MetaListFieldValidation::from_str(&validation_name), + MetaNameValueFieldValidation::from_str(&validation_name), + meta, + ) { + (Ok(validation_type), _, _, syn::Meta::Path(validation)) => { + extract_field_validator_from_nested_meta_path( + field, + validation_type, + validation, + custom_message, + rename_map, + ) + } - (_, Ok(validation_type), _, syn::Meta::List(validation)) => { - extract_validator_from_nested_meta_list( - field, - validation_type, - validation, - custom_message, - rename_map, - ) - } + (_, Ok(validation_type), _, syn::Meta::List(validation)) => { + extract_field_validator_from_nested_meta_list( + field, + validation_type, + validation, + custom_message, + rename_map, + ) + } - (_, _, Ok(validation_type), syn::Meta::NameValue(validation)) => { - extract_validator_from_nested_meta_name_value( - field, - validation_type, - validation, - custom_message, - rename_map, - ) - } + (_, _, Ok(validation_type), syn::Meta::NameValue(validation)) => { + extract_field_validator_from_nested_meta_name_value( + field, + validation_type, + validation, + custom_message, + rename_map, + ) + } - (Ok(_), _, _, _) => Err(vec![crate::Error::meta_path_validation_need_value( - validation_path, - &validation_name, - )]), + (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_list_validation_need_value( + validation_path, + &validation_name, + )]), - (_, _, Ok(_), _) => Err(vec![crate::Error::meta_name_value_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::unknown_validation_type( - validation_path, - &validation_name, - )]), - }; + _ => Err(vec![crate::Error::field_validation_type_unknown( + validation_path, + &validation_name, + )]), + }; - match validator { - Ok(validator) => { - if errors.is_empty() { - Ok(validator) - } else { - Err(errors) - } - } - Err(validator_errors) => { - errors.extend(validator_errors); + match validator { + Ok(validator) => { + if errors.is_empty() { + Ok(validator) + } else { Err(errors) } } - } else { - errors.push(crate::Error::validate_type_required_error(attribute)); - Err(errors) + Err(validator_errors) => { + errors.extend(validator_errors); + Err(errors) + } } } diff --git a/serde_valid_derive/src/validate/meta/meta_path.rs b/serde_valid_derive/src/validate/meta/meta_path.rs index 409953db..a8052b79 100644 --- a/serde_valid_derive/src/validate/meta/meta_path.rs +++ b/serde_valid_derive/src/validate/meta/meta_path.rs @@ -1,27 +1,19 @@ use crate::serde::rename::RenameMap; use crate::types::Field; use crate::validate::Validator; -use proc_macro2::TokenStream; use quote::quote; -pub fn extract_validator_from_meta_path( +pub fn extract_field_validator_from_meta_path( field: &impl Field, rename_map: &RenameMap, ) -> Result { - Ok(inner_extract_validator_from_meta_path(field, rename_map)) -} - -fn inner_extract_validator_from_meta_path( - field: &impl Field, - rename_map: &RenameMap, -) -> TokenStream { let field_ident = field.ident(); let field_name = field.name(); let field_key = field.key(); let rename = rename_map.get(field_name).unwrap_or(&field_key); let errors = field.errors_variable(); - quote!( + Ok(quote!( if let Err(__inner_errors) = #field_ident.validate() { match __inner_errors { ::serde_valid::validation::Errors::Object(__object_errors) => { @@ -39,5 +31,5 @@ fn inner_extract_validator_from_meta_path( } } } - ) + )) } diff --git a/serde_valid_derive/src/validate/meta/nested_meta_list.rs b/serde_valid_derive/src/validate/meta/nested_meta_list.rs index eea8920f..5bd249c5 100644 --- a/serde_valid_derive/src/validate/meta/nested_meta_list.rs +++ b/serde_valid_derive/src/validate/meta/nested_meta_list.rs @@ -1,23 +1,23 @@ use crate::serde::rename::RenameMap; use crate::types::Field; -use crate::validate::common::{CustomMessageToken, MetaListValidation}; +use crate::validate::common::{CustomMessageToken, MetaListFieldValidation}; use crate::validate::generic::{ extract_generic_custom_validator, extract_generic_enumerate_validator, }; use crate::validate::Validator; -pub fn extract_validator_from_nested_meta_list( +pub fn extract_field_validator_from_nested_meta_list( field: &impl Field, - validation_type: MetaListValidation, + validation_type: MetaListFieldValidation, validation: &syn::MetaList, custom_message: CustomMessageToken, rename_map: &RenameMap, ) -> Result { match validation_type { - MetaListValidation::Enumerate => { + MetaListFieldValidation::Enumerate => { extract_generic_enumerate_validator(field, validation, custom_message, rename_map) } - MetaListValidation::Custom => { + MetaListFieldValidation::Custom => { extract_generic_custom_validator(field, validation, rename_map) } } diff --git a/serde_valid_derive/src/validate/meta/nested_meta_name_value.rs b/serde_valid_derive/src/validate/meta/nested_meta_name_value.rs index bd16666a..056539f6 100644 --- a/serde_valid_derive/src/validate/meta/nested_meta_name_value.rs +++ b/serde_valid_derive/src/validate/meta/nested_meta_name_value.rs @@ -3,7 +3,7 @@ use crate::types::Field; use crate::validate::array::{ extract_array_max_items_validator, extract_array_min_items_validator, }; -use crate::validate::common::{get_lit, CustomMessageToken, MetaNameValueValidation}; +use crate::validate::common::{get_lit, CustomMessageToken, MetaNameValueFieldValidation}; use crate::validate::numeric::{ extract_numeric_exclusive_maximum_validator, extract_numeric_exclusive_minimum_validator, extract_numeric_maximum_validator, extract_numeric_minimum_validator, @@ -18,9 +18,9 @@ use crate::validate::string::{ }; use crate::validate::Validator; -pub fn extract_validator_from_nested_meta_name_value( +pub fn extract_field_validator_from_nested_meta_name_value( field: &impl Field, - validation_type: MetaNameValueValidation, + validation_type: MetaNameValueFieldValidation, validation: &syn::MetaNameValue, custom_message: CustomMessageToken, rename_map: &RenameMap, @@ -28,55 +28,59 @@ pub fn extract_validator_from_nested_meta_name_value( let validation_value = get_lit(&validation.value)?; match validation_type { - MetaNameValueValidation::Minimum => { + MetaNameValueFieldValidation::Minimum => { extract_numeric_minimum_validator(field, validation_value, custom_message, rename_map) } - MetaNameValueValidation::Maximum => { + MetaNameValueFieldValidation::Maximum => { extract_numeric_maximum_validator(field, validation_value, custom_message, rename_map) } - MetaNameValueValidation::ExclusiveMinimum => extract_numeric_exclusive_minimum_validator( - field, - validation_value, - custom_message, - rename_map, - ), - MetaNameValueValidation::ExclusiveMaximum => extract_numeric_exclusive_maximum_validator( - field, - validation_value, - custom_message, - rename_map, - ), - MetaNameValueValidation::MinLength => { + MetaNameValueFieldValidation::ExclusiveMinimum => { + extract_numeric_exclusive_minimum_validator( + field, + validation_value, + custom_message, + rename_map, + ) + } + MetaNameValueFieldValidation::ExclusiveMaximum => { + extract_numeric_exclusive_maximum_validator( + field, + validation_value, + custom_message, + rename_map, + ) + } + MetaNameValueFieldValidation::MinLength => { extract_string_min_length_validator(field, validation_value, custom_message, rename_map) } - MetaNameValueValidation::MaxLength => { + MetaNameValueFieldValidation::MaxLength => { extract_string_max_length_validator(field, validation_value, custom_message, rename_map) } - MetaNameValueValidation::MinItems => { + MetaNameValueFieldValidation::MinItems => { extract_array_min_items_validator(field, validation_value, custom_message, rename_map) } - MetaNameValueValidation::MaxItems => { + MetaNameValueFieldValidation::MaxItems => { extract_array_max_items_validator(field, validation_value, custom_message, rename_map) } - MetaNameValueValidation::MinProperties => extract_object_min_properties_validator( + MetaNameValueFieldValidation::MinProperties => extract_object_min_properties_validator( field, validation_value, custom_message, rename_map, ), - MetaNameValueValidation::MaxProperties => extract_object_max_properties_validator( + MetaNameValueFieldValidation::MaxProperties => extract_object_max_properties_validator( field, validation_value, custom_message, rename_map, ), - MetaNameValueValidation::MultipleOf => extract_numeric_multiple_of_validator( + MetaNameValueFieldValidation::MultipleOf => extract_numeric_multiple_of_validator( field, validation_value, custom_message, rename_map, ), - MetaNameValueValidation::Pattern => { + MetaNameValueFieldValidation::Pattern => { extract_string_pattern_validator(field, validation_value, custom_message, rename_map) } } diff --git a/serde_valid_derive/src/validate/meta/nested_meta_path.rs b/serde_valid_derive/src/validate/meta/nested_meta_path.rs index 5fc819bf..c8b9ffcc 100644 --- a/serde_valid_derive/src/validate/meta/nested_meta_path.rs +++ b/serde_valid_derive/src/validate/meta/nested_meta_path.rs @@ -1,18 +1,18 @@ use crate::serde::rename::RenameMap; use crate::types::Field; use crate::validate::array::extract_array_unique_items_validator; -use crate::validate::common::{CustomMessageToken, MetaPathValidation}; +use crate::validate::common::{CustomMessageToken, MetaPathFieldValidation}; use crate::validate::Validator; -pub fn extract_validator_from_nested_meta_path( +pub fn extract_field_validator_from_nested_meta_path( field: &impl Field, - validation_type: MetaPathValidation, + validation_type: MetaPathFieldValidation, _validation: &syn::Path, custom_message: CustomMessageToken, rename_map: &RenameMap, ) -> Result { match validation_type { - MetaPathValidation::UniqueItems => Ok(extract_array_unique_items_validator( + MetaPathFieldValidation::UniqueItems => Ok(extract_array_unique_items_validator( field, custom_message, rename_map,