diff --git a/serde_valid/README.md b/serde_valid/README.md index 272721f9..c1b31f3b 100644 --- a/serde_valid/README.md +++ b/serde_valid/README.md @@ -46,22 +46,22 @@ assert!(s.validate().is_ok()); Serde Valid support standard validation based JSON Schema. -| Type | Serde Valid (validate derive) | Serde Valid (validate trait) | JSON Schema | -| :-----: | :----------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------- | -| String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string#length) | -| String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string#length) | -| String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string#regexp) | -| Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -| Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -| Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -| Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -| Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric#multiples) | -| Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | -| Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | -| Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array#length) | -| Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array#length) | -| Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array#uniqueItems) | -| Generic | `#[validate(enumerate(5, 10, 15))]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/enum) | +| Type | Serde Valid (validate derive) | Serde Valid (validate trait) | JSON Schema | +| :-----: | :------------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------- | +| String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string#length) | +| String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string#length) | +| String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string#regexp) | +| Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +| Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +| Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +| Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +| Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric#multiples) | +| Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | +| Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | +| Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array#length) | +| Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array#length) | +| Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array#uniqueItems) | +| Generic | `#[validate(enumerate = [5, 10, 15])]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/enum) | In addition, [serde_valid::utils][module@crate::utils] provides a type of validation not described in the JSON schema specification. diff --git a/serde_valid/src/lib.rs b/serde_valid/src/lib.rs index 12cb1e7c..988e7cfa 100644 --- a/serde_valid/src/lib.rs +++ b/serde_valid/src/lib.rs @@ -46,22 +46,22 @@ //! //! Serde Valid support standard validation based JSON Schema. //! -//! | Type | Serde Valid (validate derive) | Serde Valid (validate trait) | JSON Schema | -//! | :-----: | :----------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------- | -//! | String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string#length) | -//! | String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string#length) | -//! | String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string#regexp) | -//! | Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -//! | Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -//! | Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -//! | Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | -//! | Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric#multiples) | -//! | Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | -//! | Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | -//! | Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array#length) | -//! | Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array#length) | -//! | Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array#uniqueItems) | -//! | Generic | `#[validate(enumerate(5, 10, 15))]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/enum) | +//! | Type | Serde Valid (validate derive) | Serde Valid (validate trait) | JSON Schema | +//! | :-----: | :------------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------- | +//! | String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string#length) | +//! | String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string#length) | +//! | String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string#regexp) | +//! | Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +//! | Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +//! | Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +//! | Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) | +//! | Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric#multiples) | +//! | Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | +//! | Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object#size) | +//! | Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array#length) | +//! | Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array#length) | +//! | Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array#uniqueItems) | +//! | Generic | `#[validate(enumerate = [5, 10, 15])]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/enum) | //! //! In addition, [serde_valid::utils][module@crate::utils] provides a type of validation not described in the JSON schema specification. //! diff --git a/serde_valid/src/validation/generic/enumerate.rs b/serde_valid/src/validation/generic/enumerate.rs index c92fbe30..81f89839 100644 --- a/serde_valid/src/validation/generic/enumerate.rs +++ b/serde_valid/src/validation/generic/enumerate.rs @@ -22,7 +22,7 @@ use crate::EnumerateError; /// /// #[derive(Validate)] /// struct TestStruct { -/// #[validate(enumerate("1", "2", "3"))] +/// #[validate(enumerate = ["1", "2", "3"])] /// val: MyType, /// } /// diff --git a/serde_valid/tests/complex_test.rs b/serde_valid/tests/complex_test.rs index e5178458..2311f860 100644 --- a/serde_valid/tests/complex_test.rs +++ b/serde_valid/tests/complex_test.rs @@ -8,7 +8,7 @@ fn sample_rule(_val: i32) -> Result<(), serde_valid::validation::Error> { #[validate(custom(|s| sample_rule(s.int_value)))] struct TestStruct<'a> { // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Numeric validator #[validate(multiple_of = 5)] #[validate(minimum = 5)] @@ -18,7 +18,7 @@ struct TestStruct<'a> { int_value: i32, // Generic validator - #[validate(enumerate(5.0, 10.0, 15.0))] + #[validate(enumerate = [5.0, 10.0, 15.0])] // Numeric validator #[validate(multiple_of = 5.0)] #[validate(minimum = 5.0)] @@ -28,7 +28,7 @@ struct TestStruct<'a> { float_value: f32, // Generic validator - #[validate(enumerate("12345", "67890"))] + #[validate(enumerate = ["12345", "67890"])] // String validator #[validate(min_length = 5)] #[validate(max_length = 5)] @@ -36,7 +36,7 @@ struct TestStruct<'a> { string_value: String, // Generic validator - #[validate(enumerate("12345", "67890"))] + #[validate(enumerate = ["12345", "67890"])] // String validator #[validate(min_length = 5)] #[validate(max_length = 5)] @@ -44,7 +44,7 @@ struct TestStruct<'a> { str_value: &'a str, // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Numeric validator #[validate(multiple_of = 5)] #[validate(minimum = 5)] @@ -52,7 +52,7 @@ struct TestStruct<'a> { optional_value: Option, // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Array validator #[validate(unique_items)] #[validate(min_items = 3)] @@ -78,7 +78,7 @@ struct TestStruct<'a> { #[derive(Debug, Validate)] struct TestInnerStruct<'a> { // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Numeric validator #[validate(multiple_of = 5)] #[validate(minimum = 5)] @@ -88,7 +88,7 @@ struct TestInnerStruct<'a> { inner_int_value: i32, // Generic validator - #[validate(enumerate(5.0, 10.0, 15.0))] + #[validate(enumerate = [5.0, 10.0, 15.0])] // Numeric validator #[validate(multiple_of = 5.0)] #[validate(minimum = 5.0)] @@ -98,7 +98,7 @@ struct TestInnerStruct<'a> { inner_float_value: f32, // Generic validator - #[validate(enumerate("12345", "67890"))] + #[validate(enumerate = ["12345", "67890"])] // String validator #[validate(min_length = 5)] #[validate(max_length = 5)] @@ -106,7 +106,7 @@ struct TestInnerStruct<'a> { inner_string_value: String, // Generic validator - #[validate(enumerate("12345", "67890"))] + #[validate(enumerate = ["12345", "67890"])] // String validator #[validate(min_length = 5)] #[validate(max_length = 5)] @@ -114,7 +114,7 @@ struct TestInnerStruct<'a> { inner_str_value: &'a str, // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Numeric validator #[validate(multiple_of = 5)] #[validate(minimum = 5)] @@ -122,7 +122,7 @@ struct TestInnerStruct<'a> { inner_optional_value: Option, // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Array validator #[validate(unique_items)] #[validate(min_items = 3)] diff --git a/serde_valid/tests/deprecated_enumerate_test.rs b/serde_valid/tests/deprecated_enumerate_test.rs new file mode 100644 index 00000000..638436c7 --- /dev/null +++ b/serde_valid/tests/deprecated_enumerate_test.rs @@ -0,0 +1,256 @@ +#[allow(deprecated)] +mod test { + use serde_json::json; + use serde_valid::{Validate, ValidateEnumerate}; + + #[test] + fn enumerate_integer_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3))] + val: i32, + } + + let s = TestStruct { val: 1 }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_float_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(0.3, 1.2, 1.5))] + val: f32, + } + + let s = TestStruct { val: 0.3 }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_str_type() { + #[derive(Validate)] + struct TestStruct<'a> { + #[validate(enumerate("a", "b"))] + val: &'a str, + } + + let s = TestStruct { val: "a" }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_string_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate("a", "b"))] + val: String, + } + + let s = TestStruct { + val: "a".to_string(), + }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_vec_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3, 4, 5))] + val: Vec, + } + + let s = TestStruct { val: vec![3, 4] }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_vec_str_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate("1", "2", "3", "4", "5"))] + val: Vec<&'static str>, + } + + let s = TestStruct { + val: vec!["3", "4"], + }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_vec_string_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate("1", "2", "3", "4", "5"))] + val: Vec, + } + + let s = TestStruct { + val: vec!["3".to_owned(), "4".to_owned()], + }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_option_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3))] + val: Option, + } + + let s = TestStruct { val: Some(3) }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_vec_option_type() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(3))] + val: Vec>, + } + + let s = TestStruct { val: vec![Some(3)] }; + assert!(s.validate().is_ok()); + } + + #[test] + fn enumerate_is_err() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(0.3, 1.2, 1.5))] + val: f32, + } + + let s = TestStruct { val: 0.1 }; + assert!(s.validate().is_err()); + } + + #[test] + fn enumerate_err_message() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3))] + val: i32, + } + + let s = TestStruct { val: 4 }; + + assert_eq!( + s.validate().unwrap_err().to_string(), + json!({ + "errors": [], + "properties": { + "val": { + "errors": [ + "The value must be in [1, 2, 3]." + ] + } + } + }) + .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 enumerate_custom_err_message() { + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3), message = "this is custom 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_numeric_trait() { + struct MyType(i32); + + impl PartialEq for MyType { + fn eq(&self, other: &i32) -> bool { + self.0.eq(other) + } + } + + impl ValidateEnumerate for MyType { + fn validate_enumerate( + &self, + enumerate: &[i32], + ) -> Result<(), serde_valid::EnumerateError> { + self.0.validate_enumerate(enumerate) + } + } + + #[derive(Validate)] + struct TestStruct { + #[validate(enumerate(1, 2, 3), message = "this is custom message.")] + val: MyType, + } + + let s = TestStruct { val: MyType(4) }; + + 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/rule_test.rs b/serde_valid/tests/deprecated_rule_test.rs similarity index 100% rename from serde_valid/tests/rule_test.rs rename to serde_valid/tests/deprecated_rule_test.rs diff --git a/serde_valid/tests/enumerate_test.rs b/serde_valid/tests/enumerate_test.rs index cc3f586c..a346eb82 100644 --- a/serde_valid/tests/enumerate_test.rs +++ b/serde_valid/tests/enumerate_test.rs @@ -5,7 +5,7 @@ use serde_valid::{Validate, ValidateEnumerate}; fn enumerate_integer_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3))] + #[validate(enumerate = [1, 2, 3])] val: i32, } @@ -17,7 +17,7 @@ fn enumerate_integer_type() { fn enumerate_float_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(0.3, 1.2, 1.5))] + #[validate(enumerate = [0.3, 1.2, 1.5])] val: f32, } @@ -29,7 +29,7 @@ fn enumerate_float_type() { fn enumerate_str_type() { #[derive(Validate)] struct TestStruct<'a> { - #[validate(enumerate("a", "b"))] + #[validate(enumerate = ["a", "b"])] val: &'a str, } @@ -41,7 +41,7 @@ fn enumerate_str_type() { fn enumerate_string_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate("a", "b"))] + #[validate(enumerate = ["a", "b"])] val: String, } @@ -55,7 +55,7 @@ fn enumerate_string_type() { fn enumerate_vec_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3, 4, 5))] + #[validate(enumerate = [1, 2, 3, 4, 5])] val: Vec, } @@ -67,7 +67,7 @@ fn enumerate_vec_type() { fn enumerate_vec_str_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate("1", "2", "3", "4", "5"))] + #[validate(enumerate = ["1", "2", "3", "4", "5"])] val: Vec<&'static str>, } @@ -81,7 +81,7 @@ fn enumerate_vec_str_type() { fn enumerate_vec_string_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate("1", "2", "3", "4", "5"))] + #[validate(enumerate = ["1", "2", "3", "4", "5"])] val: Vec, } @@ -95,7 +95,7 @@ fn enumerate_vec_string_type() { fn enumerate_option_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3))] + #[validate(enumerate = [1, 2, 3])] val: Option, } @@ -107,7 +107,7 @@ fn enumerate_option_type() { fn enumerate_vec_option_type() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(3))] + #[validate(enumerate = [3])] val: Vec>, } @@ -119,7 +119,7 @@ fn enumerate_vec_option_type() { fn enumerate_is_err() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(0.3, 1.2, 1.5))] + #[validate(enumerate = [0.3, 1.2, 1.5])] val: f32, } @@ -131,7 +131,7 @@ fn enumerate_is_err() { fn enumerate_err_message() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3))] + #[validate(enumerate = [1, 2, 3])] val: i32, } @@ -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, } @@ -187,7 +187,7 @@ fn enumerate_custom_err_message_fn() { fn enumerate_custom_err_message() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3), message = "this is custom message.")] + #[validate(enumerate = [1, 2, 3], message = "this is custom message.")] val: i32, } @@ -227,7 +227,7 @@ fn enumerate_numeric_trait() { #[derive(Validate)] struct TestStruct { - #[validate(enumerate(1, 2, 3), message = "this is custom message.")] + #[validate(enumerate = [1, 2, 3], message = "this is custom message.")] val: MyType, } diff --git a/serde_valid/tests/hashmap_test.rs b/serde_valid/tests/hashmap_test.rs index 41cb959d..1cf36794 100644 --- a/serde_valid/tests/hashmap_test.rs +++ b/serde_valid/tests/hashmap_test.rs @@ -17,7 +17,7 @@ struct TestStruct { hashmap_of_hashmap: HashMap<&'static str, HashMap>, // Generic validator - #[validate(enumerate(5, 10, 15))] + #[validate(enumerate = [5, 10, 15])] // Numeric validator #[validate(multiple_of = 5)] #[validate(minimum = 5)] diff --git a/serde_valid_derive/src/attribute.rs b/serde_valid_derive/src/attribute.rs index 5c773869..971812f7 100644 --- a/serde_valid_derive/src/attribute.rs +++ b/serde_valid_derive/src/attribute.rs @@ -118,6 +118,7 @@ enum_str! { MaxProperties = "max_properties", MultipleOf = "multiple_of", Pattern = "pattern", + Enumerate = "enumerate", } } diff --git a/serde_valid_derive/src/attribute/field_validate/field.rs b/serde_valid_derive/src/attribute/field_validate/field.rs index ee845a54..d4a0ec4a 100644 --- a/serde_valid_derive/src/attribute/field_validate/field.rs +++ b/serde_valid_derive/src/attribute/field_validate/field.rs @@ -1,5 +1,6 @@ use crate::attribute::Validator; use crate::types::Field; +use crate::warning::WithWarnings; use quote::quote; use std::borrow::Cow; use std::iter::FromIterator; @@ -7,11 +8,16 @@ use std::iter::FromIterator; pub struct FieldValidators<'a, F: Field + Clone + 'a> { field: Cow<'a, F>, validators: Vec, + pub warnings: Vec, } impl<'a, F: Field + Clone> FieldValidators<'a, F> { - pub fn new(field: Cow<'a, F>, validators: Vec) -> Self { - Self { field, validators } + pub fn new(field: Cow<'a, F>, validators: Vec>) -> Self { + Self { + field, + validators: validators.iter().map(|v| v.data.clone()).collect(), + warnings: validators.into_iter().flat_map(|v| v.warnings).collect(), + } } pub fn ident(&self) -> &syn::Ident { diff --git a/serde_valid_derive/src/attribute/field_validate/generic.rs b/serde_valid_derive/src/attribute/field_validate/generic.rs index 867a6113..26edc176 100644 --- a/serde_valid_derive/src/attribute/field_validate/generic.rs +++ b/serde_valid_derive/src/attribute/field_validate/generic.rs @@ -3,5 +3,8 @@ mod enumerate; mod validate; pub use custom::extract_generic_custom_validator; -pub use enumerate::extract_generic_enumerate_validator; +pub use enumerate::{ + extract_generic_enumerate_validator_from_list, + extract_generic_enumerate_validator_from_name_value, +}; pub use validate::extract_generic_validate_validator; diff --git a/serde_valid_derive/src/attribute/field_validate/generic/enumerate.rs b/serde_valid_derive/src/attribute/field_validate/generic/enumerate.rs index e07054d6..ad3e0638 100644 --- a/serde_valid_derive/src/attribute/field_validate/generic/enumerate.rs +++ b/serde_valid_derive/src/attribute/field_validate/generic/enumerate.rs @@ -6,18 +6,29 @@ use quote::quote; type Lits<'a> = syn::punctuated::Punctuated; -pub fn extract_generic_enumerate_validator( +pub fn extract_generic_enumerate_validator_from_list( field: &impl Field, item_list: &syn::MetaList, message_format: MessageFormat, rename_map: &RenameMap, ) -> Result { - inner_extract_generic_enumerate_validator(field, item_list, message_format, rename_map) + let lits = get_enumerate_from_list(item_list)?; + inner_extract_generic_enumerate_validator(field, &lits, message_format, rename_map) +} + +pub fn extract_generic_enumerate_validator_from_name_value( + field: &impl Field, + name_value: &syn::MetaNameValue, + message_format: MessageFormat, + rename_map: &RenameMap, +) -> Result { + let lits = get_enumerate_from_name_value(name_value)?; + inner_extract_generic_enumerate_validator(field, &lits, message_format, rename_map) } fn inner_extract_generic_enumerate_validator( field: &impl Field, - item_list: &syn::MetaList, + lits: &Lits, message_format: MessageFormat, rename_map: &RenameMap, ) -> Result { @@ -26,12 +37,11 @@ fn inner_extract_generic_enumerate_validator( let field_key = field.key(); let rename = rename_map.get(field_name).unwrap_or(&field_key); let errors = field.errors_variable(); - let enumerate = get_enumerate(item_list)?; Ok(quote!( if let Err(__composited_error_params) = ::serde_valid::validation::ValidateCompositedEnumerate::validate_composited_enumerate( #field_ident, - &[#enumerate], + &[#lits], ) { use ::serde_valid::validation::IntoError; use ::serde_valid::validation::error::FormatDefault; @@ -44,7 +54,7 @@ fn inner_extract_generic_enumerate_validator( )) } -fn get_enumerate(meta_list: &syn::MetaList) -> Result { +fn get_enumerate_from_list(meta_list: &syn::MetaList) -> Result { let mut errors = vec![]; let mut enumerate = Lits::new(); let nested = meta_list @@ -74,3 +84,20 @@ fn get_enumerate(meta_list: &syn::MetaList) -> Result { Err(errors) } } + +fn get_enumerate_from_name_value(name_value: &syn::MetaNameValue) -> Result { + if let syn::Expr::Array(array) = &name_value.value { + let mut enumerate = Lits::new(); + for item in &array.elems { + match item { + syn::Expr::Lit(lit) => enumerate.push(lit.lit.clone()), + _ => return Err(vec![crate::Error::literal_only(item)]), + } + } + Ok(enumerate) + } else { + Err(vec![crate::Error::validate_enumerate_need_array( + &name_value.value, + )]) + } +} diff --git a/serde_valid_derive/src/attribute/field_validate/generic/validate.rs b/serde_valid_derive/src/attribute/field_validate/generic/validate.rs index a74ddedf..da3f4f26 100644 --- a/serde_valid_derive/src/attribute/field_validate/generic/validate.rs +++ b/serde_valid_derive/src/attribute/field_validate/generic/validate.rs @@ -1,19 +1,20 @@ use crate::attribute::Validator; use crate::serde::rename::RenameMap; use crate::types::Field; +use crate::warning::WithWarnings; use quote::quote; pub fn extract_generic_validate_validator( field: &impl Field, rename_map: &RenameMap, -) -> Result { +) -> Result, crate::Errors> { let field_ident = field.ident(); let field_name = field.name(); let field_key = field.key(); let rename = rename_map.get(field_name).unwrap_or(&field_key); let errors = field.errors_variable(); - Ok(quote!( + Ok(WithWarnings::new(quote!( if let Err(__inner_errors) = #field_ident.validate() { match __inner_errors { ::serde_valid::validation::Errors::Object(__object_errors) => { @@ -31,5 +32,5 @@ pub fn extract_generic_validate_validator( } } } - )) + ))) } diff --git a/serde_valid_derive/src/attribute/field_validate/meta.rs b/serde_valid_derive/src/attribute/field_validate/meta.rs index fdd31a03..4f9f135d 100644 --- a/serde_valid_derive/src/attribute/field_validate/meta.rs +++ b/serde_valid_derive/src/attribute/field_validate/meta.rs @@ -11,6 +11,7 @@ use crate::attribute::{ use crate::serde::rename::RenameMap; use crate::types::Field; use crate::types::SingleIdentPath; +use crate::warning::WithWarnings; use meta_list::extract_field_validator_from_meta_list; use meta_name_value::extract_field_validator_from_meta_name_value; use meta_path::extract_field_validator_from_meta_path; @@ -22,7 +23,7 @@ pub fn extract_field_validator( field: &impl Field, attribute: &syn::Attribute, rename_map: &RenameMap, -) -> Result { +) -> Result, crate::Errors> { match &attribute.meta { syn::Meta::List(list) => inner_extract_field_validator(field, attribute, list, rename_map), syn::Meta::Path(_) => extract_generic_validate_validator(field, rename_map), @@ -39,7 +40,7 @@ fn inner_extract_field_validator( attribute: &syn::Attribute, meta_list: &syn::MetaList, rename_map: &RenameMap, -) -> Result { +) -> Result, crate::Errors> { let mut errors = vec![]; let nested = meta_list .parse_args_with(crate::types::CommaSeparatedMetas::parse_terminated) @@ -123,6 +124,7 @@ fn inner_extract_field_validator( message_format, rename_map, ) + .map(WithWarnings::new) } (Ok(_), _, _, _) => Err(vec![crate::Error::meta_path_validation_need_value( diff --git a/serde_valid_derive/src/attribute/field_validate/meta/meta_list.rs b/serde_valid_derive/src/attribute/field_validate/meta/meta_list.rs index c8568a4b..a4359f06 100644 --- a/serde_valid_derive/src/attribute/field_validate/meta/meta_list.rs +++ b/serde_valid_derive/src/attribute/field_validate/meta/meta_list.rs @@ -1,11 +1,14 @@ +use syn::spanned::Spanned; + use crate::attribute::common::message_format::MessageFormat; use crate::attribute::field_validate::generic::{ - extract_generic_custom_validator, extract_generic_enumerate_validator, + extract_generic_custom_validator, extract_generic_enumerate_validator_from_list, }; -use crate::attribute::MetaListFieldValidation; -use crate::attribute::Validator; +use crate::attribute::{MetaListFieldValidation, Validator}; use crate::serde::rename::RenameMap; use crate::types::Field; +use crate::warning::Warning; +use crate::warning::WithWarnings; pub fn extract_field_validator_from_meta_list( field: &impl Field, @@ -13,13 +16,24 @@ pub fn extract_field_validator_from_meta_list( validation: &syn::MetaList, message_format: MessageFormat, rename_map: &RenameMap, -) -> Result { +) -> Result, crate::Errors> { match validation_type { - MetaListFieldValidation::Enumerate => { - extract_generic_enumerate_validator(field, validation, message_format, rename_map) - } + MetaListFieldValidation::Enumerate => extract_generic_enumerate_validator_from_list( + field, + validation, + message_format, + rename_map, + ) + .map(|data| WithWarnings { + data, + warnings: vec![Warning::new_enumerate_path_deprecated( + field.ident(), + validation.span(), + )], + }), MetaListFieldValidation::Custom => { extract_generic_custom_validator(field, validation, message_format, rename_map) + .map(WithWarnings::new) } } } diff --git a/serde_valid_derive/src/attribute/field_validate/meta/meta_name_value.rs b/serde_valid_derive/src/attribute/field_validate/meta/meta_name_value.rs index 148fe2c7..cd638dea 100644 --- a/serde_valid_derive/src/attribute/field_validate/meta/meta_name_value.rs +++ b/serde_valid_derive/src/attribute/field_validate/meta/meta_name_value.rs @@ -3,6 +3,7 @@ use crate::attribute::common::message_format::MessageFormat; use crate::attribute::field_validate::array::{ extract_array_max_items_validator, extract_array_min_items_validator, }; +use crate::attribute::field_validate::generic::extract_generic_enumerate_validator_from_name_value; use crate::attribute::field_validate::numeric::{ extract_numeric_exclusive_maximum_validator, extract_numeric_exclusive_minimum_validator, extract_numeric_maximum_validator, extract_numeric_minimum_validator, @@ -26,16 +27,18 @@ pub fn extract_field_validator_from_meta_name_value( message_format: MessageFormat, rename_map: &RenameMap, ) -> Result { - let validation_value = get_lit(&validation.value)?; - match validation_type { MetaNameValueFieldValidation::Minimum => { + let validation_value = get_lit(&validation.value)?; + extract_numeric_minimum_validator(field, validation_value, message_format, rename_map) } MetaNameValueFieldValidation::Maximum => { + let validation_value = get_lit(&validation.value)?; extract_numeric_maximum_validator(field, validation_value, message_format, rename_map) } MetaNameValueFieldValidation::ExclusiveMinimum => { + let validation_value = get_lit(&validation.value)?; extract_numeric_exclusive_minimum_validator( field, validation_value, @@ -44,6 +47,7 @@ pub fn extract_field_validator_from_meta_name_value( ) } MetaNameValueFieldValidation::ExclusiveMaximum => { + let validation_value = get_lit(&validation.value)?; extract_numeric_exclusive_maximum_validator( field, validation_value, @@ -52,37 +56,59 @@ pub fn extract_field_validator_from_meta_name_value( ) } MetaNameValueFieldValidation::MinLength => { + let validation_value = get_lit(&validation.value)?; extract_string_min_length_validator(field, validation_value, message_format, rename_map) } MetaNameValueFieldValidation::MaxLength => { + let validation_value = get_lit(&validation.value)?; extract_string_max_length_validator(field, validation_value, message_format, rename_map) } MetaNameValueFieldValidation::MinItems => { + let validation_value = get_lit(&validation.value)?; extract_array_min_items_validator(field, validation_value, message_format, rename_map) } MetaNameValueFieldValidation::MaxItems => { + let validation_value = get_lit(&validation.value)?; extract_array_max_items_validator(field, validation_value, message_format, rename_map) } - MetaNameValueFieldValidation::MinProperties => extract_object_min_properties_validator( - field, - validation_value, - message_format, - rename_map, - ), - MetaNameValueFieldValidation::MaxProperties => extract_object_max_properties_validator( - field, - validation_value, - message_format, - rename_map, - ), - MetaNameValueFieldValidation::MultipleOf => extract_numeric_multiple_of_validator( - field, - validation_value, - message_format, - rename_map, - ), + MetaNameValueFieldValidation::MinProperties => { + let validation_value = get_lit(&validation.value)?; + extract_object_min_properties_validator( + field, + validation_value, + message_format, + rename_map, + ) + } + MetaNameValueFieldValidation::MaxProperties => { + let validation_value = get_lit(&validation.value)?; + extract_object_max_properties_validator( + field, + validation_value, + message_format, + rename_map, + ) + } + MetaNameValueFieldValidation::MultipleOf => { + let validation_value = get_lit(&validation.value)?; + extract_numeric_multiple_of_validator( + field, + validation_value, + message_format, + rename_map, + ) + } MetaNameValueFieldValidation::Pattern => { + let validation_value = get_lit(&validation.value)?; extract_string_pattern_validator(field, validation_value, message_format, rename_map) } + MetaNameValueFieldValidation::Enumerate => { + extract_generic_enumerate_validator_from_name_value( + field, + validation, + message_format, + rename_map, + ) + } } } diff --git a/serde_valid_derive/src/attribute/field_validate/meta/meta_path.rs b/serde_valid_derive/src/attribute/field_validate/meta/meta_path.rs index 6caa1708..68512755 100644 --- a/serde_valid_derive/src/attribute/field_validate/meta/meta_path.rs +++ b/serde_valid_derive/src/attribute/field_validate/meta/meta_path.rs @@ -3,6 +3,7 @@ use crate::attribute::field_validate::array::extract_array_unique_items_validato use crate::attribute::{MetaPathFieldValidation, Validator}; use crate::serde::rename::RenameMap; use crate::types::Field; +use crate::warning::WithWarnings; pub fn extract_field_validator_from_meta_path( field: &impl Field, @@ -10,12 +11,10 @@ pub fn extract_field_validator_from_meta_path( _validation: &syn::Path, message_format: MessageFormat, rename_map: &RenameMap, -) -> Result { +) -> Result, crate::Errors> { match validation_type { - MetaPathFieldValidation::UniqueItems => Ok(extract_array_unique_items_validator( - field, - message_format, - rename_map, + MetaPathFieldValidation::UniqueItems => Ok(WithWarnings::new( + extract_array_unique_items_validator(field, message_format, rename_map), )), } } diff --git a/serde_valid_derive/src/attribute/rule/named_struct_rule.rs b/serde_valid_derive/src/attribute/rule/named_struct_rule.rs index 01269a8e..9eb1ce66 100644 --- a/serde_valid_derive/src/attribute/rule/named_struct_rule.rs +++ b/serde_valid_derive/src/attribute/rule/named_struct_rule.rs @@ -5,15 +5,15 @@ use quote::{quote, ToTokens}; use syn::spanned::Spanned; use crate::{ - output_stream::OutputStream, types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams}, warning::Warning, + warning::WithWarnings, }; pub fn collect_rules_from_named_struct( ident: &syn::Ident, attributes: &[syn::Attribute], -) -> Result<(HashSet, OutputStream), crate::Errors> { +) -> Result<(HashSet, WithWarnings), crate::Errors> { let mut errors = vec![]; let mut rule_fields = HashSet::new(); @@ -50,8 +50,8 @@ pub fn collect_rules_from_named_struct( if errors.is_empty() { Ok(( rule_fields, - OutputStream { - output: TokenStream::from_iter(rules), + WithWarnings { + data: TokenStream::from_iter(rules), warnings, }, )) diff --git a/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs b/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs index 2219c18e..d514bf73 100644 --- a/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs +++ b/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs @@ -5,15 +5,15 @@ use quote::quote; use syn::spanned::Spanned; use crate::{ - output_stream::OutputStream, types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams, NestedMeta}, warning::Warning, + warning::WithWarnings, }; pub fn collect_rules_from_unnamed_struct( ident: &syn::Ident, attributes: &[syn::Attribute], -) -> Result<(HashSet, OutputStream), crate::Errors> { +) -> Result<(HashSet, WithWarnings), crate::Errors> { let mut errors = vec![]; let mut rule_fields = HashSet::new(); @@ -50,8 +50,8 @@ pub fn collect_rules_from_unnamed_struct( if errors.is_empty() { Ok(( rule_fields, - OutputStream { - output: TokenStream::from_iter(rules), + WithWarnings { + data: TokenStream::from_iter(rules), warnings, }, )) diff --git a/serde_valid_derive/src/attribute/struct_validate.rs b/serde_valid_derive/src/attribute/struct_validate.rs index c69eb434..b471bd5c 100644 --- a/serde_valid_derive/src/attribute/struct_validate.rs +++ b/serde_valid_derive/src/attribute/struct_validate.rs @@ -1,13 +1,13 @@ mod generic; mod meta; -use crate::attribute::Validator; +use crate::{attribute::Validator, warning::WithWarnings}; use self::meta::extract_struct_validator; pub fn collect_struct_custom_from_named_struct( attributes: &[syn::Attribute], -) -> Result { +) -> Result>, crate::Errors> { let mut errors = vec![]; let validations = attributes @@ -28,7 +28,7 @@ pub fn collect_struct_custom_from_named_struct( .collect::>(); if errors.is_empty() { - Ok(Validator::from_iter(validations)) + Ok(WithWarnings::from_iter(validations)) } else { Err(errors) } diff --git a/serde_valid_derive/src/attribute/struct_validate/meta.rs b/serde_valid_derive/src/attribute/struct_validate/meta.rs index f5f049aa..76367812 100644 --- a/serde_valid_derive/src/attribute/struct_validate/meta.rs +++ b/serde_valid_derive/src/attribute/struct_validate/meta.rs @@ -9,6 +9,7 @@ use crate::{ Validator, }, types::SingleIdentPath, + warning::WithWarnings, }; use quote::quote; use std::str::FromStr; @@ -19,10 +20,14 @@ use self::{ meta_path::extract_struct_validator_from_meta_path, }; -pub fn extract_struct_validator(attribute: &syn::Attribute) -> Result { +pub fn extract_struct_validator( + attribute: &syn::Attribute, +) -> Result, crate::Errors> { match &attribute.meta { - syn::Meta::Path(_) => Ok(quote!()), - syn::Meta::List(list) => inner_extract_struct_validator(attribute, list), + syn::Meta::Path(_) => Ok(WithWarnings::new(quote!())), + syn::Meta::List(list) => { + inner_extract_struct_validator(attribute, list).map(WithWarnings::new) + } syn::Meta::NameValue(name_value) => { Err(vec![crate::Error::validate_meta_name_value_not_supported( name_value, diff --git a/serde_valid_derive/src/derive/enum_derive.rs b/serde_valid_derive/src/derive/enum_derive.rs index 6b243065..2099a706 100644 --- a/serde_valid_derive/src/derive/enum_derive.rs +++ b/serde_valid_derive/src/derive/enum_derive.rs @@ -2,10 +2,11 @@ use super::named_struct_derive::collect_named_fields_validators_list; use super::unnamed_struct_derive::collect_unnamed_fields_validators_list; use crate::attribute::rule::{collect_rules_from_named_struct, collect_rules_from_unnamed_struct}; use crate::attribute::variant_validate::collect_variant_custom_from_variant; +use crate::attribute::Validator; use crate::error::{array_errors_tokens, new_type_errors_tokens, object_errors_tokens}; -use crate::output_stream::OutputStream; use crate::serde::rename::collect_serde_rename_map; use crate::types::CommaSeparatedTokenStreams; +use crate::warning::WithWarnings; use proc_macro2::TokenStream; use quote::quote; use std::collections::HashSet; @@ -34,7 +35,7 @@ pub fn expand_enum_validate_derive( Ok(variant_varidates_and_rules) => variant_varidates_and_rules, Err(variant_errors) => { errors.extend(variant_errors); - OutputStream::new() + WithWarnings::new(Validator::new()) } } } @@ -48,18 +49,18 @@ pub fn expand_enum_validate_derive( Ok(variant_varidates_and_rules) => variant_varidates_and_rules, Err(variant_errors) => { errors.extend(variant_errors); - OutputStream::new() + WithWarnings::new(Validator::new()) } } } - syn::Fields::Unit => OutputStream::new(), + syn::Fields::Unit => WithWarnings::new(Validator::new()), }) .collect::>(); let validations_and_rules = TokenStream::from_iter( validations .iter() - .map(|variant| variant.output.clone()) + .map(|variant| variant.data.clone()) .collect::>(), ); let warnings = validations @@ -90,7 +91,7 @@ fn expand_enum_variant_named_fields_validation( input: &syn::DeriveInput, variant: &syn::Variant, named_fields: &syn::FieldsNamed, -) -> Result { +) -> Result, crate::Errors> { let mut errors = vec![]; let variant_ident = &variant.ident; @@ -99,15 +100,15 @@ fn expand_enum_variant_named_fields_validation( let ( rule_fields, - OutputStream { - output: rules, + WithWarnings { + data: rules, warnings, }, ) = match collect_rules_from_named_struct(&variant.ident, &variant.attrs) { Ok(field_rules) => field_rules, Err(variant_errors) => { errors.extend(variant_errors); - (HashSet::new(), OutputStream::new()) + (HashSet::new(), WithWarnings::new(Validator::new())) } }; @@ -146,8 +147,8 @@ fn expand_enum_variant_named_fields_validation( let variant_errors = object_errors_tokens(); if errors.is_empty() { - Ok(OutputStream { - output: quote!( + Ok(WithWarnings { + data: quote!( if let #ident::#variant_ident{#fields_idents} = &self { let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); @@ -173,7 +174,7 @@ fn expand_enum_variant_unnamed_fields_varidation( input: &syn::DeriveInput, variant: &syn::Variant, unnamed_fields: &syn::FieldsUnnamed, -) -> Result { +) -> Result, crate::Errors> { let mut errors = vec![]; let variant_ident = &variant.ident; @@ -181,15 +182,15 @@ fn expand_enum_variant_unnamed_fields_varidation( let ( rule_fields, - OutputStream { - output: rules, + WithWarnings { + data: rules, warnings, }, ) = match collect_rules_from_unnamed_struct(&variant.ident, &variant.attrs) { Ok(field_rules) => field_rules, Err(variant_errors) => { errors.extend(variant_errors); - (HashSet::new(), OutputStream::new()) + (HashSet::new(), WithWarnings::new(Validator::new())) } }; @@ -232,8 +233,8 @@ fn expand_enum_variant_unnamed_fields_varidation( }; if errors.is_empty() { - Ok(OutputStream { - output: quote!( + Ok(WithWarnings { + data: quote!( if let #ident::#variant_ident(#fields_idents) = &self { let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); diff --git a/serde_valid_derive/src/derive/named_struct_derive.rs b/serde_valid_derive/src/derive/named_struct_derive.rs index ded49cef..d886f4df 100644 --- a/serde_valid_derive/src/derive/named_struct_derive.rs +++ b/serde_valid_derive/src/derive/named_struct_derive.rs @@ -1,10 +1,11 @@ use crate::attribute::field_validate::{extract_field_validator, FieldValidators}; use crate::attribute::rule::collect_rules_from_named_struct; use crate::attribute::struct_validate::collect_struct_custom_from_named_struct; +use crate::attribute::Validator; use crate::error::object_errors_tokens; -use crate::output_stream::OutputStream; use crate::serde::rename::{collect_serde_rename_map, RenameMap}; use crate::types::{Field, NamedField}; +use crate::warning::WithWarnings; use proc_macro2::TokenStream; use quote::quote; use std::borrow::Cow; @@ -23,19 +24,23 @@ pub fn expand_named_struct_derive( let ( rule_fields, - OutputStream { - output: rules, - warnings, + WithWarnings { + data: rules, + mut warnings, }, ) = match collect_rules_from_named_struct(&input.ident, &input.attrs) { Ok((rule_fields, rules)) => (rule_fields, rules), Err(rule_errors) => { errors.extend(rule_errors); - (HashSet::new(), OutputStream::new()) + (HashSet::new(), WithWarnings::new(Validator::new())) } }; + let struct_validations = match collect_struct_custom_from_named_struct(&input.attrs) { - Ok(validations) => TokenStream::from_iter(validations), + Ok(validations) => { + warnings.extend(validations.warnings); + TokenStream::from_iter(validations.data) + } Err(rule_errors) => { errors.extend(rule_errors); quote!() @@ -44,6 +49,7 @@ pub fn expand_named_struct_derive( let field_validates = match collect_named_fields_validators_list(fields, &rename_map) { Ok(field_validators) => TokenStream::from_iter(field_validators.iter().map(|validator| { + warnings.extend(validator.warnings.clone()); if validator.is_empty() && rule_fields.contains(validator.ident()) { validator.get_field_variable_token() } else { diff --git a/serde_valid_derive/src/derive/unnamed_struct_derive.rs b/serde_valid_derive/src/derive/unnamed_struct_derive.rs index f0e0bc60..7071049d 100644 --- a/serde_valid_derive/src/derive/unnamed_struct_derive.rs +++ b/serde_valid_derive/src/derive/unnamed_struct_derive.rs @@ -1,9 +1,10 @@ use crate::attribute::field_validate::{extract_field_validator, FieldValidators}; use crate::attribute::rule::collect_rules_from_unnamed_struct; use crate::attribute::struct_validate::collect_struct_custom_from_named_struct; +use crate::attribute::Validator; use crate::error::{array_errors_tokens, new_type_errors_tokens}; -use crate::output_stream::OutputStream; use crate::types::{Field, UnnamedField}; +use crate::warning::WithWarnings; use proc_macro2::TokenStream; use quote::quote; use std::borrow::Cow; @@ -21,28 +22,32 @@ pub fn expand_unnamed_struct_derive( let ( rule_fields, - OutputStream { - output: rules, - warnings, + WithWarnings { + data: rules, + mut warnings, }, ) = match collect_rules_from_unnamed_struct(&input.ident, &input.attrs) { Ok((rule_fields, rules)) => (rule_fields, rules), Err(rule_errors) => { errors.extend(rule_errors); - (HashSet::new(), OutputStream::new()) + (HashSet::new(), WithWarnings::new(Validator::new())) } }; let struct_validations = match collect_struct_custom_from_named_struct(&input.attrs) { - Ok(validations) => TokenStream::from_iter(validations), + Ok(validations) => { + warnings.extend(validations.warnings); + Validator::from_iter(validations.data) + } Err(rule_errors) => { errors.extend(rule_errors); quote!() } }; - let field_validates = match collect_unnamed_fields_validators_list(fields) { + let field_validates: TokenStream = match collect_unnamed_fields_validators_list(fields) { Ok(field_validators) => TokenStream::from_iter(field_validators.iter().map(|validator| { + warnings.extend(validator.warnings.clone()); if validator.is_empty() && rule_fields.contains(validator.ident()) { validator.get_field_variable_token() } else { diff --git a/serde_valid_derive/src/error.rs b/serde_valid_derive/src/error.rs index 172181f5..0735fdfd 100644 --- a/serde_valid_derive/src/error.rs +++ b/serde_valid_derive/src/error.rs @@ -3,6 +3,7 @@ use crate::attribute::{ MetaNameValueCustomMessage, MetaNameValueFieldValidation, MetaNameValueStructValidation, MetaPathCustomMessage, MetaPathFieldValidation, MetaPathStructValidation, }; +use itertools::Itertools; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::spanned::Spanned; @@ -303,6 +304,7 @@ impl Error { let candidates = &(MetaPathFieldValidation::iter().map(|x| x.name())) .chain(MetaListFieldValidation::iter().map(|x| x.name())) .chain(MetaNameValueFieldValidation::iter().map(|x| x.name())) + .unique() .collect::>(); let filterd_candidates = @@ -367,6 +369,13 @@ impl Error { Self::new(path.span(), "#[validate(enumerate(???))] needs items.") } + pub fn validate_enumerate_need_array(path: impl Spanned) -> Self { + Self::new( + path.span(), + "#[validate(enumerate = ???)] needs literal array only.", + ) + } + pub fn validate_custom_need_function_or_closure(span: impl Spanned) -> Self { Self::new( span.span(), diff --git a/serde_valid_derive/src/lib.rs b/serde_valid_derive/src/lib.rs index 69c1180f..77319c71 100644 --- a/serde_valid_derive/src/lib.rs +++ b/serde_valid_derive/src/lib.rs @@ -2,7 +2,6 @@ mod attribute; mod derive; mod error; -mod output_stream; mod serde; mod types; mod warning; diff --git a/serde_valid_derive/src/output_stream.rs b/serde_valid_derive/src/output_stream.rs deleted file mode 100644 index 478c23c7..00000000 --- a/serde_valid_derive/src/output_stream.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::warning::Warning; -use proc_macro2::TokenStream; - -#[derive(Debug, Clone)] -pub struct OutputStream { - pub output: TokenStream, - pub warnings: Vec, -} - -impl OutputStream { - pub fn new() -> Self { - Self { - output: TokenStream::new(), - warnings: vec![], - } - } - - #[allow(unused)] - pub fn extend_warnings(&mut self, warnings: Vec) -> &mut Self { - self.warnings.extend(warnings); - self - } -} diff --git a/serde_valid_derive/src/warning.rs b/serde_valid_derive/src/warning.rs index cb22d3da..b634cc6a 100644 --- a/serde_valid_derive/src/warning.rs +++ b/serde_valid_derive/src/warning.rs @@ -4,6 +4,54 @@ use proc_macro2::Span; use proc_macro2::TokenStream; use quote::{quote_spanned, ToTokens}; +#[derive(Debug, Clone)] +pub struct WithWarnings { + pub data: T, + pub warnings: Vec, +} + +impl WithWarnings { + pub fn new(data: T) -> Self { + Self { + data, + warnings: vec![], + } + } + + pub fn from_iter(data: impl IntoIterator>) -> WithWarnings> { + let mut warnings = vec![]; + let data = data + .into_iter() + .map(|WithWarnings { data, warnings: w }| { + warnings.extend(w); + data + }) + .collect::>(); + WithWarnings { data, warnings } + } + + #[allow(unused)] + pub fn extend_warnings(&mut self, warnings: Vec) -> &mut Self { + self.warnings.extend(warnings); + self + } +} + +impl From> for WithWarnings> { + fn from(with_warnings: WithWarnings) -> Self { + WithWarnings { + data: vec![with_warnings.data], + warnings: with_warnings.warnings, + } + } +} + +impl From for WithWarnings { + fn from(data: T) -> Self { + Self::new(data) + } +} + #[derive(Debug, Clone)] pub enum Warning { Deprecated { @@ -49,7 +97,17 @@ impl Warning { pub fn new_rule_deprecated(ident: &syn::Ident, span: Span) -> Self { Self::Deprecated { ident: ident.clone(), - note: "#[rule(...)] is deprecated, use #[validate(custom(...)))] instead".to_string(), + note: "#[rule(...)] is deprecated, please use #[validate(custom(...)))] instead." + .to_string(), + span, + } + } + + pub fn new_enumerate_path_deprecated(ident: &syn::Ident, span: Span) -> Self { + Self::Deprecated { + ident: ident.clone(), + note: "#[validate(enumerate(...))] is deprecated, please use #[validate(enumerate = [...])] instead." + .to_string(), span, } }