From 815c6cefef2a64fc948d29e6840d44ee9c5297ab Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 22 Jun 2024 11:42:30 +0900 Subject: [PATCH] feat: #[validate(..., message_fn(???)] to #[validate(..., message_fn = ???)]. --- docs/src/Attributes/custom_message.md | 2 +- serde_valid/README.md | 2 +- serde_valid/src/lib.rs | 2 +- .../tests/deprecated_enumerate_test.rs | 2 +- .../tests/deprecated_message_fn_test.rs | 315 ++++++++++++++++++ serde_valid/tests/enumerate_test.rs | 2 +- serde_valid/tests/items_test.rs | 4 +- serde_valid/tests/length_test.rs | 4 +- serde_valid/tests/multiple_of_test.rs | 2 +- serde_valid/tests/pattern_test.rs | 2 +- serde_valid/tests/properties_test.rs | 4 +- serde_valid/tests/range_test.rs | 4 +- serde_valid/tests/unique_items_test.rs | 2 +- serde_valid_derive/src/attribute.rs | 1 + .../src/attribute/common/message_format.rs | 30 +- serde_valid_derive/src/error.rs | 15 +- 16 files changed, 369 insertions(+), 24 deletions(-) create mode 100644 serde_valid/tests/deprecated_message_fn_test.rs diff --git a/docs/src/Attributes/custom_message.md b/docs/src/Attributes/custom_message.md index 27220e4c..825ad8c8 100644 --- a/docs/src/Attributes/custom_message.md +++ b/docs/src/Attributes/custom_message.md @@ -15,7 +15,7 @@ fn min_error_message(_params: &serde_valid::MinItemsError) -> String { #[derive(Validate)] struct Data ( - #[validate(min_items = 4, message_fn(min_error_message))] + #[validate(min_items = 4, message_fn = min_error_message)] #[validate(max_items = 2, message = "this is max custom message.")] Vec, ); diff --git a/serde_valid/README.md b/serde_valid/README.md index d2f1bd29..13404336 100644 --- a/serde_valid/README.md +++ b/serde_valid/README.md @@ -144,7 +144,7 @@ fn min_error_message(_params: &serde_valid::MinItemsError) -> String { #[derive(Validate)] struct Data { - #[validate(min_items = 4, message_fn(min_error_message))] + #[validate(min_items = 4, message_fn = min_error_message)] #[validate(max_items = 2, message = "this is custom message.")] val: Vec, } diff --git a/serde_valid/src/lib.rs b/serde_valid/src/lib.rs index eb2178ff..8ea62c33 100644 --- a/serde_valid/src/lib.rs +++ b/serde_valid/src/lib.rs @@ -144,7 +144,7 @@ //! //! #[derive(Validate)] //! struct Data { -//! #[validate(min_items = 4, message_fn(min_error_message))] +//! #[validate(min_items = 4, message_fn = min_error_message)] //! #[validate(max_items = 2, message = "this is custom message.")] //! val: Vec, //! } diff --git a/serde_valid/tests/deprecated_enumerate_test.rs b/serde_valid/tests/deprecated_enumerate_test.rs index 638436c7..792b6c9b 100644 --- a/serde_valid/tests/deprecated_enumerate_test.rs +++ b/serde_valid/tests/deprecated_enumerate_test.rs @@ -163,7 +163,7 @@ mod test { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3), message_fn(error_message))] + #[validate(enumerate(1, 2, 3), message_fn = error_message)] val: i32, } diff --git a/serde_valid/tests/deprecated_message_fn_test.rs b/serde_valid/tests/deprecated_message_fn_test.rs new file mode 100644 index 00000000..bd781805 --- /dev/null +++ b/serde_valid/tests/deprecated_message_fn_test.rs @@ -0,0 +1,315 @@ +#[allow(deprecated)] +mod test { + + use serde_json::json; + use serde_valid::Validate; + + use serde::Deserialize; + + #[test] + fn deprecated_enumerate_custom_err_message_fn() { + fn error_message(_params: &serde_valid::error::EnumerateError) -> String { + "this is custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3), message_fn(error_message))] + val: i32, + } + + let s = TestStruct { val: 4 }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn enumerate_custom_err_message_fn() { + fn error_message(_params: &serde_valid::error::EnumerateError) -> String { + "this is custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate = [1, 2, 3], message_fn(error_message))] + val: i32, + } + + let s = TestStruct { val: 4 }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn items_custom_err_message_fn() { + #[inline] + fn min_error_message(_params: &serde_valid::MinItemsError) -> String { + "this is min custom message.".to_string() + } + #[inline] + fn max_error_message(_params: &serde_valid::MaxItemsError) -> String { + "this is max custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(min_items = 4, message_fn(min_error_message))] + #[validate(max_items = 2, message_fn(max_error_message))] + val: Vec, + } + + let s = TestStruct { val: vec![1, 2, 3] }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is min custom message.", + "this is max custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn length_custom_err_message_fn() { + fn custom_min_error_message(_params: &serde_valid::MinLengthError) -> String { + "this is min custom message.".to_string() + } + + fn custom_max_error_message(_params: &serde_valid::MaxLengthError) -> String { + "this is max custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(min_length = 5, message_fn(custom_min_error_message))] + #[validate(max_length = 3, message_fn(custom_max_error_message))] + val: String, + } + + let s = TestStruct { + val: String::from("test"), + }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is min custom message.", + "this is max custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn multiple_of_custom_err_message_fn() { + fn error_message(_params: &serde_valid::MultipleOfError) -> String { + "this is custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(multiple_of = 5, message_fn(error_message))] + val: i32, + } + + let s = TestStruct { val: 14 }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn pattern_custom_err_message_fn() { + fn error_message(_params: &serde_valid::PatternError) -> String { + "this is custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(pattern = r"^\d{4}-\d{2}-\d{2}$", message_fn = error_message)] + val: String, + } + + let s = TestStruct { + val: String::from("2020/09/10"), + }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn properties_custom_err_message_fn() { + fn min_custom_error_message(_params: &serde_valid::MinPropertiesError) -> String { + "this is min custom message.".to_string() + } + + fn max_custom_error_message(_params: &serde_valid::MaxPropertiesError) -> String { + "this is max custom message.".to_string() + } + + #[derive(Deserialize, Validate)] + struct TestStruct { + #[validate(min_properties = 3, message_fn(min_custom_error_message))] + #[validate(max_properties = 1, message_fn(max_custom_error_message))] + val: serde_json::Map, + } + + let s: TestStruct = serde_json::from_value(json!({ + "val": { + "key1": "value1", + "key2": "value2", + } + })) + .unwrap(); + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is min custom message.", + "this is max custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn range_custom_err_message_fn() { + fn custom_min_error_message(_params: &serde_valid::MinimumError) -> String { + "this is min custom message.".to_string() + } + + fn custom_max_error_message(_params: &serde_valid::MaximumError) -> String { + "this is max custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(minimum = 5, message_fn(custom_min_error_message))] + #[validate(maximum = 3, message_fn(custom_max_error_message))] + val: i32, + } + + let s = TestStruct { val: 4 }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is min custom message.", + "this is max custom message." + ] + } + } + }) + .to_string() + ); + } + + #[test] + fn unique_items_custom_err_message_fn() { + fn error_message(_params: &serde_valid::UniqueItemsError) -> String { + "this is custom message.".to_string() + } + + #[derive(Validate)] + struct TestStruct { + #[validate(unique_items, message_fn(error_message))] + val: Vec, + } + + let s = TestStruct { + val: vec![1, 2, 3, 2], + }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "this is custom message." + ] + } + } + }) + .to_string() + ); + } +} diff --git a/serde_valid/tests/enumerate_test.rs b/serde_valid/tests/enumerate_test.rs index a346eb82..cd504cee 100644 --- a/serde_valid/tests/enumerate_test.rs +++ b/serde_valid/tests/enumerate_test.rs @@ -161,7 +161,7 @@ fn enumerate_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate = [1, 2, 3], message_fn(error_message))] + #[validate(enumerate = [1, 2, 3], message_fn = error_message)] val: i32, } diff --git a/serde_valid/tests/items_test.rs b/serde_valid/tests/items_test.rs index f03081d1..2af90bfc 100644 --- a/serde_valid/tests/items_test.rs +++ b/serde_valid/tests/items_test.rs @@ -179,8 +179,8 @@ fn items_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(min_items = 4, message_fn(min_error_message))] - #[validate(max_items = 2, message_fn(max_error_message))] + #[validate(min_items = 4, message_fn = min_error_message)] + #[validate(max_items = 2, message_fn = max_error_message)] val: Vec, } diff --git a/serde_valid/tests/length_test.rs b/serde_valid/tests/length_test.rs index 57854dae..a816cef9 100644 --- a/serde_valid/tests/length_test.rs +++ b/serde_valid/tests/length_test.rs @@ -310,8 +310,8 @@ fn length_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(min_length = 5, message_fn(custom_min_error_message))] - #[validate(max_length = 3, message_fn(custom_max_error_message))] + #[validate(min_length = 5, message_fn = custom_min_error_message)] + #[validate(max_length = 3, message_fn = custom_max_error_message)] val: String, } diff --git a/serde_valid/tests/multiple_of_test.rs b/serde_valid/tests/multiple_of_test.rs index 550014de..e8e12aee 100644 --- a/serde_valid/tests/multiple_of_test.rs +++ b/serde_valid/tests/multiple_of_test.rs @@ -149,7 +149,7 @@ fn multiple_of_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(multiple_of = 5, message_fn(error_message))] + #[validate(multiple_of = 5, message_fn = error_message)] val: i32, } diff --git a/serde_valid/tests/pattern_test.rs b/serde_valid/tests/pattern_test.rs index f69e6d49..7e55c23f 100644 --- a/serde_valid/tests/pattern_test.rs +++ b/serde_valid/tests/pattern_test.rs @@ -226,7 +226,7 @@ fn pattern_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(pattern = r"^\d{4}-\d{2}-\d{2}$", message_fn(error_message))] + #[validate(pattern = r"^\d{4}-\d{2}-\d{2}$", message_fn = error_message)] val: String, } diff --git a/serde_valid/tests/properties_test.rs b/serde_valid/tests/properties_test.rs index abb8254d..e1029137 100644 --- a/serde_valid/tests/properties_test.rs +++ b/serde_valid/tests/properties_test.rs @@ -182,8 +182,8 @@ fn properties_custom_err_message_fn() { #[derive(Deserialize, Validate)] struct TestStruct { - #[validate(min_properties = 3, message_fn(min_custom_error_message))] - #[validate(max_properties = 1, message_fn(max_custom_error_message))] + #[validate(min_properties = 3, message_fn = min_custom_error_message)] + #[validate(max_properties = 1, message_fn = max_custom_error_message)] val: serde_json::Map, } diff --git a/serde_valid/tests/range_test.rs b/serde_valid/tests/range_test.rs index 326ad869..e7f6bafa 100644 --- a/serde_valid/tests/range_test.rs +++ b/serde_valid/tests/range_test.rs @@ -294,8 +294,8 @@ fn range_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(minimum = 5, message_fn(custom_min_error_message))] - #[validate(maximum = 3, message_fn(custom_max_error_message))] + #[validate(minimum = 5, message_fn = custom_min_error_message)] + #[validate(maximum = 3, message_fn = custom_max_error_message)] val: i32, } diff --git a/serde_valid/tests/unique_items_test.rs b/serde_valid/tests/unique_items_test.rs index 2290786c..e5e4be65 100644 --- a/serde_valid/tests/unique_items_test.rs +++ b/serde_valid/tests/unique_items_test.rs @@ -75,7 +75,7 @@ fn unique_items_custom_err_message_fn() { #[derive(Validate)] struct TestStruct { - #[validate(unique_items, message_fn(error_message))] + #[validate(unique_items, message_fn = error_message)] val: Vec, } diff --git a/serde_valid_derive/src/attribute.rs b/serde_valid_derive/src/attribute.rs index 4858522e..6e022775 100644 --- a/serde_valid_derive/src/attribute.rs +++ b/serde_valid_derive/src/attribute.rs @@ -148,5 +148,6 @@ enum_str! { enum_str! { pub enum MetaNameValueCustomMessage { Message = "message", + MessageFn = "message_fn", } } diff --git a/serde_valid_derive/src/attribute/common/message_format.rs b/serde_valid_derive/src/attribute/common/message_format.rs index 111b85ee..5148dbe6 100644 --- a/serde_valid_derive/src/attribute/common/message_format.rs +++ b/serde_valid_derive/src/attribute/common/message_format.rs @@ -68,7 +68,7 @@ fn extract_custom_message_format_from_meta_list( .map_err(|error| vec![crate::Error::custom_message_parse_error(path_ident, &error)])?; match custom_message_type { - MetaListCustomMessage::MessageFn => get_message_fn(path, &message_fn_define), + MetaListCustomMessage::MessageFn => get_message_fn_from_meta_list(path, &message_fn_define), #[cfg(feature = "fluent")] message_type @ (MetaListCustomMessage::I18n | MetaListCustomMessage::Fluent) => { get_fluent_message(message_type, path, &message_fn_define) @@ -82,30 +82,50 @@ fn extract_custom_message_format_from_name_value( ) -> Result { match custom_message_type { MetaNameValueCustomMessage::Message => get_message(&name_value.value), + MetaNameValueCustomMessage::MessageFn => get_message_fn_from_meta_name_value(name_value), } } -fn get_message_fn( +fn get_message_fn_from_meta_list( path: &syn::Path, fn_define: &CommaSeparatedNestedMetas, ) -> Result { let fn_name = match fn_define.len() { - 0 => Err(vec![crate::Error::message_fn_need_item(path)]), + 0 => Err(vec![crate::Error::message_fn_meta_list_need_item(path)]), 1 => match &fn_define[0] { NestedMeta::Meta(syn::Meta::Path(fn_name)) => Some(quote!(#fn_name)), _ => None, } - .ok_or_else(|| vec![crate::Error::message_fn_allow_name_path(&fn_define[0])]), + .ok_or_else(|| { + vec![crate::Error::message_fn_meta_list_allow_name_path( + &fn_define[0], + )] + }), _ => Err(fn_define .iter() .skip(1) - .map(crate::Error::message_fn_tail_error) + .map(crate::Error::message_fn_meta_list_tail_error) .collect()), }?; Ok(quote!(::serde_valid::validation::error::Format::MessageFn(#fn_name))) } +fn get_message_fn_from_meta_name_value( + meta_name_value: &syn::MetaNameValue, +) -> Result { + let fn_define = match &meta_name_value.value { + syn::Expr::Path(syn::ExprPath { path, .. }) => quote!(#path), + syn::Expr::Call(call) => quote!(#call), + syn::Expr::Closure(closure) => quote!(#closure), + _ => Err(vec![ + crate::Error::message_fn_meta_name_value_needs_function_or_closure(meta_name_value), + ])?, + }; + + Ok(quote!(::serde_valid::validation::error::Format::MessageFn(#fn_define))) +} + fn get_message(expr: &syn::Expr) -> Result { match expr { syn::Expr::Lit(lit) => { diff --git a/serde_valid_derive/src/error.rs b/serde_valid_derive/src/error.rs index 03df7c8c..7f5fe7c8 100644 --- a/serde_valid_derive/src/error.rs +++ b/serde_valid_derive/src/error.rs @@ -405,27 +405,36 @@ impl Error { ) } - pub fn message_fn_need_item(path: &syn::Path) -> Self { + pub fn message_fn_meta_list_need_item(path: &syn::Path) -> Self { Self::new( path.span(), "#[validate(..., message_fn(???))] needs function.", ) } - pub fn message_fn_allow_name_path(nested_meta: &crate::types::NestedMeta) -> Self { + pub fn message_fn_meta_list_allow_name_path(nested_meta: &crate::types::NestedMeta) -> Self { Self::new( nested_meta.span(), "#[validate(..., message_fn(???))] allows only function name path.", ) } - pub fn message_fn_tail_error(nested_meta: &crate::types::NestedMeta) -> Self { + pub fn message_fn_meta_list_tail_error(nested_meta: &crate::types::NestedMeta) -> Self { Self::new( nested_meta.span(), "#[validate(..., message_fn(???))] allows only 1 item.", ) } + pub fn message_fn_meta_name_value_needs_function_or_closure( + meta_name_value: &syn::MetaNameValue, + ) -> Self { + Self::new( + meta_name_value.span(), + "#[validate(..., message_fn = ???)] needs function or closure.", + ) + } + #[cfg(feature = "fluent")] pub fn fluent_need_item(message_type: &MetaListCustomMessage, path: &syn::Path) -> Self { Self::new(