Skip to content

Commit

Permalink
feat: support FnOnce<T> -> Result<(), Vec<Error>>.
Browse files Browse the repository at this point in the history
  • Loading branch information
yassun7010 committed Jun 19, 2024
1 parent e8b0a78 commit af6e65d
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 22 deletions.
12 changes: 0 additions & 12 deletions serde_valid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,18 +656,6 @@ where

pub use serde_valid_derive::Validate;

#[doc(hidden)]
pub mod helpers {
/// This function is used to avoid [rustc(E0282)](https://doc.rust-lang.org/error_codes/E0282.html) error in `#[validate(custom)]` validator on the struct.
#[inline]
pub fn wrap_closure_validation<T>(
data: &T,
f: impl FnOnce(&T) -> Result<(), crate::validation::Error>,
) -> Result<(), crate::validation::Error> {
f(data)
}
}

#[cfg(test)]
pub mod tests {
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
Expand Down
1 change: 1 addition & 0 deletions serde_valid/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod array;
mod composited;
pub mod custom;
pub mod error;
mod generic;
mod numeric;
Expand Down
74 changes: 74 additions & 0 deletions serde_valid/src/validation/custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/// This function is used to avoid [rustc(E0282)](https://doc.rust-lang.org/error_codes/E0282.html) error in `#[validate(custom)]` validator on the struct.
#[inline]
pub fn wrap_closure_validation<T, M: ToMultiple>(
data: T,
f: impl FnOnce(T) -> Result<(), M>,
) -> Result<(), Vec<crate::validation::Error>> {
f(data).map_err(|e| e.to_multiple())
}

pub trait ToMultiple {
fn to_multiple(self) -> Vec<crate::validation::Error>;
}

impl ToMultiple for Vec<crate::validation::Error> {
fn to_multiple(self) -> Vec<crate::validation::Error> {
self
}
}

impl ToMultiple for crate::validation::Error {
fn to_multiple(self) -> Vec<crate::validation::Error> {
vec![self]
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_custom_fn_single_error() {
fn single_error(data: &i32) -> Result<(), crate::validation::Error> {
if *data > 0 {
Ok(())
} else {
Err(crate::validation::Error::Custom(
"Value must be greater than 0".to_string(),
))
}
}

assert!(wrap_closure_validation(&1i32, single_error).is_ok());
}

#[test]
fn test_custom_fn_multiple_errors() {
fn multiple_errors(data: &i32) -> Result<(), Vec<crate::validation::Error>> {
let mut errors = Vec::new();
if *data > 0 {
return Ok(());
} else {
errors.push(crate::validation::Error::Custom(
"Value must be greater than 0".to_string(),
));
}

if *data < 10 {
return Ok(());
} else {
errors.push(crate::validation::Error::Custom(
"Value must be less than 10".to_string(),
));
}

if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}

assert!(wrap_closure_validation(&1i32, multiple_errors).is_ok());
}
}
87 changes: 87 additions & 0 deletions serde_valid/tests/custom_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,90 @@ fn unnamed_struct_custom_closure_is_err() {
assert_eq!(s.0, 5);
assert!(s.validate().is_err());
}

#[test]
fn named_struct_custom_vec_errors_is_ok() {
fn validation(_val: &TestStruct) -> Result<(), Vec<serde_valid::validation::Error>> {
Ok(())
}

#[derive(Validate)]
#[validate(custom(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_vec_errors_is_err() {
fn validation(_val: &TestStruct) -> Result<(), Vec<serde_valid::validation::Error>> {
Err(vec![
serde_valid::validation::Error::Custom("Error 1".to_owned()),
serde_valid::validation::Error::Custom("Error 2".to_owned()),
])
}

#[derive(Validate)]
#[validate(custom(validation))]
struct TestStruct {
val: i32,
}

let s = TestStruct { val: 5 };

assert_eq!(s.val, 5);
assert_eq!(
s.validate().unwrap_err().to_string(),
json!({
"errors": ["Error 1", "Error 2"],
})
.to_string()
);
}

#[test]
fn named_struct_custom_closure_vec_errors_is_ok() {
fn sample_struct_validation(_val: i32) -> Result<(), Vec<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 named_struct_custom_closure_vec_errors_is_err() {
fn sample_struct_validation(_val: i32) -> Result<(), Vec<serde_valid::validation::Error>> {
Err(vec![
serde_valid::validation::Error::Custom("Error 1".to_owned()),
serde_valid::validation::Error::Custom("Error 2".to_owned()),
])
}

#[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_eq!(
s.validate().unwrap_err().to_string(),
json!({
"errors": ["Error 1", "Error 2"],
})
.to_string()
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ fn extract_struct_custom_from_meta_path(meta_path: &syn::Path) -> Result<Validat
let rule_fn_name = &meta_path;

Ok(quote!(
if let Err(__error) = #rule_fn_name(self) {
__rule_vec_errors.push(__error);
if let Err(__errors) = serde_valid::validation::custom::wrap_closure_validation(self, #rule_fn_name) {
__rule_vec_errors.extend(__errors);
};
))
}
Expand All @@ -63,8 +63,8 @@ fn extract_struct_custom_from_meta_list(
meta_list: &syn::MetaList,
) -> Result<Validator, crate::Errors> {
Ok(quote!(
if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #meta_list) {
__rule_vec_errors.push(__error);
if let Err(__errors) = serde_valid::validation::custom::wrap_closure_validation(self, #meta_list) {
__rule_vec_errors.extend(__errors);
};
))
}
Expand All @@ -73,8 +73,8 @@ fn extract_struct_custom_from_closure(
closure: &syn::ExprClosure,
) -> Result<Validator, crate::Errors> {
Ok(quote!(
if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #closure) {
__rule_vec_errors.push(__error);
if let Err(__errors) = serde_valid::validation::custom::wrap_closure_validation(self, #closure) {
__rule_vec_errors.extend(__errors);
};
))
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ fn extract_variant_custom_from_meta_path(
let rule_fn_name = &meta_path;

Ok(quote!(
if let Err(__error) = #rule_fn_name(self) {
__rule_vec_errors.push(__error);
if let Err(__errors) = serde_valid::validation::custom::wrap_closure_validation(self, #rule_fn_name) {
__rule_vec_errors.extend(__errors);
};
))
}
Expand All @@ -65,8 +65,8 @@ fn extract_variant_custom_from_meta_list(
meta_list: &syn::MetaList,
) -> Result<Validator, crate::Errors> {
Ok(quote!(
if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #meta_list) {
__rule_vec_errors.push(__error);
if let Err(__errors) = serde_valid::validation::custom::wrap_closure_validation(self, #meta_list) {
__rule_vec_errors.extend(__errors);
};
))
}
Expand Down

0 comments on commit af6e65d

Please sign in to comment.