diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index b2b226428..2c7c95620 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -26,7 +26,7 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result BorrowedLifetimes { } } -fn validate_body(cont: &Container, body: Stmts) -> syn::Result { +fn validate_body(cont: &Container, body: Stmts) -> TokenStream { let serde = cont.attrs.serde_path(); - match (cont.attrs.validate(), cont.attrs.validator()) { - (None, false) => Ok(quote! { #body }), - (Some(validate), false) => Ok(quote! { - let body = { #body }?; - #validate(&body).map_err(#serde::de::Error::custom)?; - Ok(body) - }), - (None, true) => Ok(quote! { + let mut validations: Vec = cont + .attrs + .validate() + .iter() + .map(|validate| { + quote! { + #validate(&body).map_err(#serde::de::Error::custom)?; + } + }) + .collect(); + if cont.attrs.validator() { + validations.push( + quote! {validator::Validate::validate(&body).map_err(#serde::de::Error::custom)?;}, + ); + } + if validations.is_empty() { + quote! { #body } + } else { + quote! { let body = { #body }?; - validator::Validate::validate(&body).map_err(#serde::de::Error::custom)?; + #(#validations)* Ok(body) - }), - // If both `validator` and `validate` are defined, error will be reported - // on the #[serde(validate = "...")] - // ^^^^^ - (Some(validate), true) => Err(syn::Error::new( - validate.span(), - "can not define both `validator` and `validate`", - )), + } } } diff --git a/serde_derive/src/internals/attr.rs b/serde_derive/src/internals/attr.rs index 48a734242..fa4697ae6 100644 --- a/serde_derive/src/internals/attr.rs +++ b/serde_derive/src/internals/attr.rs @@ -169,7 +169,7 @@ pub struct Container { remote: Option, identifier: Identifier, serde_path: Option, - validate: Option, + validate: Vec, validator: bool, is_packed: bool, /// Error message generated when type can't be deserialized @@ -258,7 +258,7 @@ impl Container { let mut remote = Attr::none(cx, REMOTE); let mut field_identifier = BoolAttr::none(cx, FIELD_IDENTIFIER); let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER); - let mut validate = Attr::none(cx, VALIDATE); + let mut validate = VecAttr::none(cx, VALIDATE); let mut validator = BoolAttr::none(cx, VALIDATOR); let mut serde_path = Attr::none(cx, CRATE); let mut expecting = Attr::none(cx, EXPECTING); @@ -493,7 +493,7 @@ impl Container { } else if meta.path == VALIDATE { // #[serde(validate = "...")] if let Some(path) = parse_lit_into_expr_path(cx, VALIDATE, &meta)? { - validate.set(&meta.path, path); + validate.insert(&meta.path, path); } } else if meta.path == VALIDATOR { // #[serde(validator)] @@ -611,7 +611,7 @@ impl Container { self.remote.as_ref() } - pub fn validate(&self) -> Option<&syn::ExprPath> { + pub fn validate(&self) -> &[syn::ExprPath] { self.validate.as_ref() } diff --git a/test_suite/tests/test_validate.rs b/test_suite/tests/test_validate.rs index a23989f8b..ccef50858 100644 --- a/test_suite/tests/test_validate.rs +++ b/test_suite/tests/test_validate.rs @@ -3,18 +3,26 @@ use serde_test::{assert_de_tokens, assert_de_tokens_error, Token}; use std::fmt::Display; #[derive(Debug, PartialEq, Serialize, Deserialize)] -#[serde(validate = "validate_struct")] +#[serde(validate = "validate_struct1")] +#[serde(validate = "validate_struct2")] struct Struct { a: u16, } -fn validate_struct(deserialized: &Struct) -> Result<(), impl Display> { +fn validate_struct1(deserialized: &Struct) -> Result<(), impl Display> { if deserialized.a == 0 { return Err("field `a` can not be zero"); } Ok(()) } +fn validate_struct2(deserialized: &Struct) -> Result<(), impl Display> { + if deserialized.a == 2 { + return Err("field `a` can not be two"); + } + Ok(()) +} + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(validate = "validate_tuple_struct")] struct TupleStruct(u16); @@ -53,6 +61,19 @@ fn test_struct() { ], "field `a` can not be zero", ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "Struct", + len: 1, + }, + Token::Str("a"), + Token::U16(2), + Token::StructEnd, + ], + "field `a` can not be two", + ); } #[test] @@ -179,11 +200,19 @@ struct ValidateStruct { #[derive(Debug, PartialEq, validator::Validate, Deserialize)] #[serde(validator)] +#[serde(validate = "validator_struct_name")] struct ValidatorStruct { #[validate(email)] mail: String, } +fn validator_struct_name(deserialized: &ValidatorStruct) -> Result<(), impl Display> { + if deserialized.mail.starts_with("foo@") { + return Err("name can not be 'foo'"); + } + Ok(()) +} + #[test] fn test_validate_struct() { assert_de_tokens( @@ -244,4 +273,17 @@ fn test_validator_struct() { ], "mail: Validation error: email [{\"value\": String(\"email.example.com\")}]", ); + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "ValidatorStruct", + len: 1, + }, + Token::Str("mail"), + Token::Str("foo@example.com"), + Token::StructEnd, + ], + "name can not be 'foo'", + ); } diff --git a/test_suite/tests/ui/validate/both_validator_and_validate.rs b/test_suite/tests/ui/validate/both_validator_and_validate.rs deleted file mode 100644 index 66ccdce01..000000000 --- a/test_suite/tests/ui/validate/both_validator_and_validate.rs +++ /dev/null @@ -1,11 +0,0 @@ -use serde_derive::Deserialize; - -#[derive(validator::Validate, Deserialize)] -#[serde(validate = "validator::Validate::validate")] -#[serde(validator)] -struct ValidatorStruct { - #[validate(email)] - mail: String, -} - -fn main() {} diff --git a/test_suite/tests/ui/validate/both_validator_and_validate.stderr b/test_suite/tests/ui/validate/both_validator_and_validate.stderr deleted file mode 100644 index 330ec5fa9..000000000 --- a/test_suite/tests/ui/validate/both_validator_and_validate.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: can not define both `validator` and `validate` - --> tests/ui/validate/both_validator_and_validate.rs:4:20 - | -4 | #[serde(validate = "validator::Validate::validate")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/test_suite/tests/ui/validate/validator_unimplemented.rs b/test_suite/tests/ui/validate/validator_unimplemented.rs deleted file mode 100644 index 5f854045c..000000000 --- a/test_suite/tests/ui/validate/validator_unimplemented.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde_derive::Deserialize; - -#[derive(Deserialize)] -#[serde(validator)] -struct ValidatorStruct { - mail: String, -} - -fn main() {} diff --git a/test_suite/tests/ui/validate/validator_unimplemented.stderr b/test_suite/tests/ui/validate/validator_unimplemented.stderr deleted file mode 100644 index a161cbf1e..000000000 --- a/test_suite/tests/ui/validate/validator_unimplemented.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0277]: the trait bound `ValidatorStruct: Validate` is not satisfied - --> tests/ui/validate/validator_unimplemented.rs:3:10 - | -3 | #[derive(Deserialize)] - | ^^^^^^^^^^^ the trait `Validate` is not implemented for `ValidatorStruct` - | - = help: the following other types implement trait `Validate`: - &BTreeMap - &HashMap - &T - BTreeSet - BinaryHeap - HashSet - LinkedList - Vec - and $N others - = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)