Skip to content

Commit

Permalink
support #[serde(validator)]
Browse files Browse the repository at this point in the history
  • Loading branch information
zao111222333 committed Feb 12, 2025
1 parent 77f2f67 commit cb2cf61
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 13 deletions.
25 changes: 18 additions & 7 deletions serde_derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result<To
let params = Parameters::new(&cont);
let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params);
let body = Stmts(deserialize_body(&cont, &params));
let vaildated_body = validate_body(&cont, body);
let vaildated_body = validate_body(&cont, body)?;
let delife = params.borrowed.de_lifetime();
let serde = cont.attrs.serde_path();

Expand Down Expand Up @@ -274,16 +274,27 @@ fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
}
}

fn validate_body(cont: &Container, body: Stmts) -> TokenStream {
fn validate_body(cont: &Container, body: Stmts) -> syn::Result<TokenStream> {
let serde = cont.attrs.serde_path();
if let Some(validate) = cont.attrs.validate() {
quote! {
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)
}
} else {
quote! { #body }
}),
(None, true) => Ok(quote! {
let body = { #body }?;
validator::Validate::validate(&body).map_err(#serde::de::Error::custom)?;
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`",
)),
}
}

Expand Down
10 changes: 10 additions & 0 deletions serde_derive/src/internals/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub struct Container {
identifier: Identifier,
serde_path: Option<syn::Path>,
validate: Option<syn::ExprPath>,
validator: bool,
is_packed: bool,
/// Error message generated when type can't be deserialized
expecting: Option<String>,
Expand Down Expand Up @@ -258,6 +259,7 @@ impl Container {
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 validator = BoolAttr::none(cx, VALIDATOR);
let mut serde_path = Attr::none(cx, CRATE);
let mut expecting = Attr::none(cx, EXPECTING);
let mut non_exhaustive = false;
Expand Down Expand Up @@ -493,6 +495,9 @@ impl Container {
if let Some(path) = parse_lit_into_expr_path(cx, VALIDATE, &meta)? {
validate.set(&meta.path, path);
}
} else if meta.path == VALIDATOR {
// #[serde(validator)]
validator.set_true(meta.path);
} else if meta.path == EXPECTING {
// #[serde(expecting = "a message")]
if let Some(s) = get_lit_str(cx, EXPECTING, &meta)? {
Expand Down Expand Up @@ -547,6 +552,7 @@ impl Container {
identifier: decide_identifier(cx, item, field_identifier, variant_identifier),
serde_path: serde_path.get(),
validate: validate.get(),
validator: validator.get(),
is_packed,
expecting: expecting.get(),
non_exhaustive,
Expand Down Expand Up @@ -609,6 +615,10 @@ impl Container {
self.validate.as_ref()
}

pub fn validator(&self) -> bool {
self.validator
}

pub fn is_packed(&self) -> bool {
self.is_packed
}
Expand Down
1 change: 1 addition & 0 deletions serde_derive/src/internals/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub const TRY_FROM: Symbol = Symbol("try_from");
pub const UNTAGGED: Symbol = Symbol("untagged");
pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
pub const VALIDATE: Symbol = Symbol("validate");
pub const VALIDATOR: Symbol = Symbol("validator");
pub const WITH: Symbol = Symbol("with");

impl PartialEq<Symbol> for Ident {
Expand Down
50 changes: 44 additions & 6 deletions test_suite/tests/test_validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,20 +172,58 @@ fn test_tuple_variant() {

#[derive(Debug, PartialEq, validator::Validate, Deserialize)]
#[serde(validate = "validator::Validate::validate")]
struct ValidatorDemo {
struct ValidateStruct {
#[validate(email)]
mail: String,
}

#[derive(Debug, PartialEq, validator::Validate, Deserialize)]
#[serde(validator)]
struct ValidatorStruct {
#[validate(email)]
mail: String,
}

#[test]
fn test_validate_struct() {
assert_de_tokens(
&ValidateStruct {
mail: "[email protected]".into(),
},
&[
Token::Struct {
name: "ValidateStruct",
len: 1,
},
Token::Str("mail"),
Token::Str("[email protected]"),
Token::StructEnd,
],
);

assert_de_tokens_error::<ValidateStruct>(
&[
Token::Struct {
name: "ValidateStruct",
len: 1,
},
Token::Str("mail"),
Token::Str("email.example.com"),
Token::StructEnd,
],
"mail: Validation error: email [{\"value\": String(\"email.example.com\")}]",
);
}

#[test]
fn test_validator_demo() {
fn test_validator_struct() {
assert_de_tokens(
&ValidatorDemo {
&ValidatorStruct {
mail: "[email protected]".into(),
},
&[
Token::Struct {
name: "ValidatorDemo",
name: "ValidatorStruct",
len: 1,
},
Token::Str("mail"),
Expand All @@ -194,10 +232,10 @@ fn test_validator_demo() {
],
);

assert_de_tokens_error::<ValidatorDemo>(
assert_de_tokens_error::<ValidatorStruct>(
&[
Token::Struct {
name: "ValidatorDemo",
name: "ValidatorStruct",
len: 1,
},
Token::Str("mail"),
Expand Down
11 changes: 11 additions & 0 deletions test_suite/tests/ui/validate/both_validator_and_validate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use serde_derive::Deserialize;

#[derive(validator::Validate, Deserialize)]
#[serde(validate = "validator::Validate::validate")]
#[serde(validator)]
struct ValidatorStruct {
#[validate(email)]
mail: String,
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: can not define both `validator` and `validate`
--> tests/ui/validate/both_validator_and_validate.rs:4:20
|
4 | #[serde(validate = "validator::Validate::validate")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9 changes: 9 additions & 0 deletions test_suite/tests/ui/validate/validator_unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use serde_derive::Deserialize;

#[derive(Deserialize)]
#[serde(validator)]
struct ValidatorStruct {
mail: String,
}

fn main() {}
17 changes: 17 additions & 0 deletions test_suite/tests/ui/validate/validator_unimplemented.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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<K, V>
&HashMap<K, V, S>
&T
BTreeSet<T>
BinaryHeap<T>
HashSet<T>
LinkedList<T>
Vec<T>
and $N others
= note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit cb2cf61

Please sign in to comment.