diff --git a/.gitignore b/.gitignore index 30ec920f..a87e037d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.idea target/ Cargo.lock +testing-bugs/ diff --git a/validator_derive/src/lib.rs b/validator_derive/src/lib.rs index 136f7be7..0ffa74a6 100644 --- a/validator_derive/src/lib.rs +++ b/validator_derive/src/lib.rs @@ -180,11 +180,13 @@ impl ToTokens for ValidateField { }; // Custom validation - let custom = if let Some(custom) = self.custom.clone() { - custom_tokens(custom, &field_name, &field_name_str) - } else { - quote!() - }; + let mut custom = quote!(); + for c in &self.custom { + let tokens = custom_tokens(c.clone(), &field_name, &field_name_str); + custom = quote!( + #tokens + ); + } let nested = if let Some(n) = self.nested { if n { diff --git a/validator_derive/src/types.rs b/validator_derive/src/types.rs index a925a5fe..2c77d304 100644 --- a/validator_derive/src/types.rs +++ b/validator_derive/src/types.rs @@ -79,7 +79,8 @@ pub struct ValidateField { pub required_nested: Option>, pub url: Option>, pub regex: Option, - pub custom: Option, + #[darling(multiple)] + pub custom: Vec, pub skip: Option, pub nested: Option, } @@ -89,9 +90,9 @@ impl ValidateField { let field_name = self.ident.clone().expect("Field is not a named field").to_string(); let field_attrs = ¤t_field.attrs; - if let Some(custom) = &self.custom { + for c in &self.custom { // If function is not a path - if let Err(e) = &custom.function { + if let Err(e) = &c.function { abort!( e.span(), "Invalid attribute #[validate(custom(...))] on field `{}`:", field_name; note = "Invalid argument for `custom` validator, only paths are allowed"; diff --git a/validator_derive_tests/tests/custom.rs b/validator_derive_tests/tests/custom.rs index 3d9187a5..d5d0d3fe 100644 --- a/validator_derive_tests/tests/custom.rs +++ b/validator_derive_tests/tests/custom.rs @@ -4,6 +4,10 @@ fn valid_custom_fn(_: &String) -> Result<(), ValidationError> { Ok(()) } +fn another_valid_custom_fn(_: &String) -> Result<(), ValidationError> { + Ok(()) +} + fn invalid_custom_fn(_: &String) -> Result<(), ValidationError> { Err(ValidationError::new("meh")) } @@ -21,6 +25,19 @@ fn can_validate_custom_fn_ok() { assert!(s.validate().is_ok()); } +#[test] +fn can_validate_multiple_custom_fn_ok() { + #[derive(Debug, Validate)] + struct TestStruct { + #[validate(custom(function = valid_custom_fn), custom(function=another_valid_custom_fn))] + val: String, + } + + let s = TestStruct { val: "hello".to_string() }; + + assert!(s.validate().is_ok()); +} + #[test] fn can_fail_custom_fn_validation() { #[derive(Debug, Validate)]