diff --git a/CHANGELOG.md b/CHANGELOG.md index cdee5137..ff1810ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased - Implement `AsRegex` for `std::sync::LazyLock` +- Bug fix for nested issue with custom only running nested if outer passes ## 0.19.0 (2024/11/03) diff --git a/validator_derive/src/tokens/nested.rs b/validator_derive/src/tokens/nested.rs index 3af6ffbf..b74aab4d 100644 --- a/validator_derive/src/tokens/nested.rs +++ b/validator_derive/src/tokens/nested.rs @@ -5,6 +5,8 @@ pub fn nested_tokens( field_name_str: &str, ) -> proc_macro2::TokenStream { quote! { - errors.merge_self(#field_name_str, (&#field_name).validate()); + if let std::collections::hash_map::Entry::Vacant(entry) = errors.0.entry(#field_name_str) { + errors.merge_self(#field_name_str, (&#field_name).validate()); + } } } diff --git a/validator_derive_tests/tests/nested.rs b/validator_derive_tests/tests/nested.rs index 3f3b33ee..ee1abd4c 100644 --- a/validator_derive_tests/tests/nested.rs +++ b/validator_derive_tests/tests/nested.rs @@ -847,58 +847,6 @@ fn test_field_validations_take_priority_over_nested_validations() { } } -#[test] -#[should_panic(expected = "Attempt to replace non-empty ValidationErrors entry")] -#[allow(unused)] -fn test_field_validation_errors_replaced_with_nested_validations_fails() { - #[derive(Debug)] - struct ParentWithOverridingStructValidations { - child: Vec, - } - - #[derive(Debug, Validate, Serialize)] - struct Child { - #[validate(length(min = 1))] - value: String, - } - - impl Validate for ParentWithOverridingStructValidations { - // Evaluating structs after fields validations have discovered errors should fail because - // field validations are expected to take priority over nested struct validations - #[allow(unused_mut)] - fn validate(&self) -> Result<(), ValidationErrors> { - // First validate the length of the vector: - let mut errors = ValidationErrors::new(); - if !self.child.validate_length(Some(2u64), None, None) { - let mut err = ValidationError::new("length"); - err.add_param(Cow::from("min"), &2u64); - err.add_param(Cow::from("value"), &&self.child); - errors.add("child", err); - } - - // Then validate the nested vector of structs without checking for existing field errors: - let mut result = if errors.is_empty() { Ok(()) } else { Err(errors) }; - { - let results: Vec<_> = self - .child - .iter() - .map(|child| { - let mut result = Ok(()); - result = ValidationErrors::merge(result, "child", child.validate()); - result - }) - .collect(); - result = ValidationErrors::merge_all(result, "child", results); - } - result - } - } - - let instance = - ParentWithOverridingStructValidations { child: vec![Child { value: String::new() }] }; - instance.validate(); -} - #[test] #[should_panic( expected = "Attempt to add field validation to a non-Field ValidationErrorsKind instance"