From 186f94996b90dd0e7d741874358cbc2a9e790d43 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Mon, 13 May 2024 23:39:04 +0900 Subject: [PATCH] feat: add duration validation. --- serde_valid/src/lib.rs | 1 + serde_valid/src/utils.rs | 6 ++ serde_valid/src/utils/duration.rs | 65 +++++++++++++ serde_valid/tests/duration_test.rs | 91 +++++++++++++++++++ .../field_validate/generic/custom.rs | 1 + .../struct_validate/generic/custom.rs | 13 +++ 6 files changed, 177 insertions(+) create mode 100644 serde_valid/src/utils.rs create mode 100644 serde_valid/src/utils/duration.rs create mode 100644 serde_valid/tests/duration_test.rs diff --git a/serde_valid/src/lib.rs b/serde_valid/src/lib.rs index 3144cb5..b150244 100644 --- a/serde_valid/src/lib.rs +++ b/serde_valid/src/lib.rs @@ -591,6 +591,7 @@ pub use error::{ MaxLengthError, MaxPropertiesError, MaximumError, MinItemsError, MinLengthError, MinPropertiesError, MinimumError, MultipleOfError, PatternError, UniqueItemsError, }; +pub mod utils; pub use validation::{ ValidateEnumerate, ValidateExclusiveMaximum, ValidateExclusiveMinimum, ValidateMaxItems, ValidateMaxLength, ValidateMaxProperties, ValidateMaximum, ValidateMinItems, ValidateMinLength, diff --git a/serde_valid/src/utils.rs b/serde_valid/src/utils.rs new file mode 100644 index 0000000..1e076f2 --- /dev/null +++ b/serde_valid/src/utils.rs @@ -0,0 +1,6 @@ +mod duration; + +pub use duration::duration_exclusive_maximum; +pub use duration::duration_exclusive_minimum; +pub use duration::duration_maximum; +pub use duration::duration_minimum; diff --git a/serde_valid/src/utils/duration.rs b/serde_valid/src/utils/duration.rs new file mode 100644 index 0000000..bec9c25 --- /dev/null +++ b/serde_valid/src/utils/duration.rs @@ -0,0 +1,65 @@ +use std::time::Duration; + +#[allow(dead_code)] +pub fn duration_maximum( + maximum: Duration, +) -> impl FnOnce(&Duration) -> Result<(), crate::validation::Error> { + move |val: &Duration| { + if *val <= maximum { + Ok(()) + } else { + Err(crate::validation::Error::Custom(format!( + "Duration {:?} is greater than maximum {:?}.", + val, maximum + ))) + } + } +} + +#[allow(dead_code)] +pub fn duration_minimum( + minimum: Duration, +) -> impl FnOnce(&Duration) -> Result<(), crate::validation::Error> { + move |val: &Duration| { + if *val >= minimum { + Ok(()) + } else { + Err(crate::validation::Error::Custom(format!( + "Duration {:?} is less than minimum {:?}.", + val, minimum + ))) + } + } +} + +#[allow(dead_code)] +pub fn duration_exclusive_maximum( + maximum: Duration, +) -> impl FnOnce(&Duration) -> Result<(), crate::validation::Error> { + move |val: &Duration| { + if *val < maximum { + Ok(()) + } else { + Err(crate::validation::Error::Custom(format!( + "Duration {:?} is greater than or equal to exclusive maximum {:?}.", + val, maximum + ))) + } + } +} + +#[allow(dead_code)] +pub fn duration_exclusive_minimum( + minimum: Duration, +) -> impl FnOnce(&Duration) -> Result<(), crate::validation::Error> { + move |val: &Duration| { + if *val > minimum { + Ok(()) + } else { + Err(crate::validation::Error::Custom(format!( + "Duration {:?} is less than or equal to exclusive minimum {:?}.", + val, minimum + ))) + } + } +} diff --git a/serde_valid/tests/duration_test.rs b/serde_valid/tests/duration_test.rs new file mode 100644 index 0000000..82bb29e --- /dev/null +++ b/serde_valid/tests/duration_test.rs @@ -0,0 +1,91 @@ +use std::time::Duration; + +use serde_json::json; +use serde_valid::utils::{duration_maximum, duration_minimum}; +use serde_valid::Validate; + +#[test] +fn duration_maximum_is_ok() { + #[derive(Validate)] + struct TestStruct { + #[validate(custom(duration_maximum(Duration::from_micros(5))))] + val: Duration, + } + + let s = TestStruct { + val: Duration::from_micros(5), + }; + + assert!(s.validate().is_ok()); +} + +#[test] +fn duration_minimum_is_ok() { + #[derive(Validate)] + struct TestStruct { + #[validate(custom(duration_minimum(Duration::from_micros(5))))] + val: Duration, + } + + let s = TestStruct { + val: Duration::from_secs(5), + }; + + assert!(s.validate().is_ok()); +} + +#[test] +fn duration_maximum_is_err() { + #[derive(Validate)] + struct TestStruct { + #[validate(custom(duration_maximum(Duration::from_micros(5))))] + val: Duration, + } + + let s = TestStruct { + val: Duration::from_micros(10), + }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "Duration 10µs is greater than maximum 5µs." + ] + } + } + }) + .to_string() + ); +} + +#[test] +fn duration_minimum_is_err() { + #[derive(Validate)] + struct TestStruct { + #[validate(custom(duration_minimum(Duration::from_micros(5))))] + val: Duration, + } + + let s = TestStruct { + val: Duration::from_micros(1), + }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "Duration 1µs is less than minimum 5µs." + ] + } + } + }) + .to_string() + ); +} diff --git a/serde_valid_derive/src/attribute/field_validate/generic/custom.rs b/serde_valid_derive/src/attribute/field_validate/generic/custom.rs index 9d78567..6627a19 100644 --- a/serde_valid_derive/src/attribute/field_validate/generic/custom.rs +++ b/serde_valid_derive/src/attribute/field_validate/generic/custom.rs @@ -49,6 +49,7 @@ fn extract_custom_fn_name( ) -> Result { match nested_meta { crate::types::NestedMeta::Meta(syn::Meta::Path(fn_name)) => Ok(quote!(#fn_name)), + crate::types::NestedMeta::Meta(syn::Meta::List(closure)) => Ok(quote!(#closure)), crate::types::NestedMeta::Closure(closure) => Ok(quote!((#closure))), _ => Err(vec![ crate::Error::validate_custom_need_function_or_closure(nested_meta), diff --git a/serde_valid_derive/src/attribute/struct_validate/generic/custom.rs b/serde_valid_derive/src/attribute/struct_validate/generic/custom.rs index 40c7b7d..f4167ba 100644 --- a/serde_valid_derive/src/attribute/struct_validate/generic/custom.rs +++ b/serde_valid_derive/src/attribute/struct_validate/generic/custom.rs @@ -28,6 +28,9 @@ pub fn extract_generic_struct_custom_validator( crate::types::NestedMeta::Meta(syn::Meta::Path(path)) => { extract_struct_custom_from_meta_path(path) } + crate::types::NestedMeta::Meta(syn::Meta::List(list)) => { + extract_struct_custom_from_meta_list(list) + } crate::types::NestedMeta::Closure(closure) => extract_struct_custom_from_closure(closure), _ => Err(vec![ crate::Error::validate_custom_need_function_or_closure(&nested[0]), @@ -56,6 +59,16 @@ fn extract_struct_custom_from_meta_path(meta_path: &syn::Path) -> Result Result { + Ok(quote!( + if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #meta_list) { + __rule_vec_errors.push(__error); + }; + )) +} + fn extract_struct_custom_from_closure( closure: &syn::ExprClosure, ) -> Result {