diff --git a/Cargo.lock b/Cargo.lock index 782b34e2..0982f655 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,6 +201,41 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.77", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.77", +] + [[package]] name = "deranged" version = "0.3.11" @@ -248,6 +283,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -288,7 +329,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn", + "syn 2.0.77", ] [[package]] @@ -316,6 +357,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -593,6 +640,30 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -637,7 +708,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -707,6 +778,7 @@ dependencies = [ "jsonschema", "pretty_assertions", "ref-cast", + "regex", "rust_decimal", "schemars_derive", "semver", @@ -719,6 +791,7 @@ dependencies = [ "trybuild", "url", "uuid", + "validator", ] [[package]] @@ -729,7 +802,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.77", ] [[package]] @@ -764,7 +837,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -775,7 +848,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -799,7 +872,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -859,6 +932,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.77" @@ -1016,6 +1105,36 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +[[package]] +name = "validator" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" +dependencies = [ + "idna", + "once_cell", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" +dependencies = [ + "darling", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "version_check" version = "0.9.5" @@ -1050,7 +1169,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -1072,7 +1191,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1206,5 +1325,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml index 38f44f72..0fa72382 100644 --- a/schemars/Cargo.toml +++ b/schemars/Cargo.toml @@ -42,6 +42,8 @@ snapbox = { version = "0.6.17", features = ["json"] } serde_repr = "0.1.19" # Use github source until published garde version supports `length(equal = ...)` attr garde = { git = "https://github.com/jprochazk/garde.git", rev = "be00ddddf8de14530ee890ccfdbaf0b13fb32852", features = ["derive", "email", "regex", "url"] } +validator = { version = "0.18.1", features = ["derive"] } +regex = { version = "1.10.6", default-features = false } arrayvec07 = { version = "0.7", default-features = false, features = ["serde"], package = "arrayvec"} bytes1 = { version = "1.0", default-features = false, features = ["serde"], package = "bytes" } diff --git a/schemars/tests/expected/validate_inner.json b/schemars/tests/expected/validate_inner.json deleted file mode 100644 index a77e0d75..00000000 --- a/schemars/tests/expected/validate_inner.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "Struct", - "type": "object", - "properties": { - "array_str_length": { - "type": "array", - "items": { - "type": "string", - "minLength": 5, - "maxLength": 100 - }, - "minItems": 2, - "maxItems": 2 - }, - "slice_str_contains": { - "type": "array", - "items": { - "type": "string", - "pattern": "substring\\.\\.\\." - } - }, - "vec_str_regex": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[Hh]ello\\b" - } - }, - "vec_str_length": { - "type": "array", - "items": { - "type": "string", - "minLength": 1, - "maxLength": 100 - } - }, - "vec_str_length2": { - "type": "array", - "items": { - "type": "string", - "minLength": 1, - "maxLength": 100 - }, - "minItems": 1, - "maxItems": 3 - }, - "vec_str_url": { - "type": "array", - "items": { - "type": "string", - "format": "uri" - } - }, - "vec_i32_range": { - "type": "array", - "items": { - "type": "integer", - "format": "int32", - "minimum": -10, - "maximum": 10 - } - } - }, - "required": [ - "array_str_length", - "slice_str_contains", - "vec_str_regex", - "vec_str_length", - "vec_str_length2", - "vec_str_url", - "vec_i32_range" - ] -} \ No newline at end of file diff --git a/schemars/tests/expected/validate_newtype.json b/schemars/tests/expected/validate_newtype.json deleted file mode 100644 index cd835f53..00000000 --- a/schemars/tests/expected/validate_newtype.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "NewType", - "type": "integer", - "format": "uint8", - "minimum": 0, - "maximum": 10 -} \ No newline at end of file diff --git a/schemars/tests/expected/validate_tuple.json b/schemars/tests/expected/validate_tuple.json deleted file mode 100644 index fa812242..00000000 --- a/schemars/tests/expected/validate_tuple.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "Tuple", - "type": "array", - "prefixItems": [ - { - "type": "integer", - "format": "uint8", - "minimum": 0, - "maximum": 10 - }, - { - "type": "boolean" - } - ], - "minItems": 2, - "maxItems": 2 -} \ No newline at end of file diff --git a/schemars/tests/integration/main.rs b/schemars/tests/integration/main.rs index 024e26cc..10319d7c 100644 --- a/schemars/tests/integration/main.rs +++ b/schemars/tests/integration/main.rs @@ -32,6 +32,7 @@ mod same_name; #[cfg(feature = "semver1")] mod semver; mod std_types; +mod validator; mod prelude { pub(crate) use crate::test; diff --git a/schemars/tests/expected/validate_schemars_attrs.json b/schemars/tests/integration/snapshots/schemars/tests/integration/validator.rs~validate_attrs.de.json similarity index 61% rename from schemars/tests/expected/validate_schemars_attrs.json rename to schemars/tests/integration/snapshots/schemars/tests/integration/validator.rs~validate_attrs.de.json index 1f2e3eb3..f4e1fc44 100644 --- a/schemars/tests/expected/validate_schemars_attrs.json +++ b/schemars/tests/integration/snapshots/schemars/tests/integration/validator.rs~validate_attrs.de.json @@ -1,33 +1,25 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "Struct2", + "title": "ValidateAttrStruct", "type": "object", "properties": { "min_max": { "type": "number", "format": "float", - "minimum": 0.01, - "maximum": 100 + "minimum": 1.0, + "maximum": 100.0 }, "min_max2": { "type": "number", "format": "float", - "minimum": 1, - "maximum": 1000 - }, - "regex_str1": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str2": { - "type": "string", - "pattern": "^\\d+$" + "minimum": 1.0, + "maximum": 10.0 }, - "contains_str1": { + "regex_str": { "type": "string", - "pattern": "substring\\.\\.\\." + "pattern": "^[Hh]ello" }, - "contains_str2": { + "contains_str": { "type": "string", "pattern": "substring\\.\\.\\." }, @@ -44,16 +36,10 @@ "minLength": 1, "maxLength": 100 }, - "non_empty_str2": { - "type": "string", - "minLength": 1, - "maxLength": 1000 - }, "pair": { "type": "array", "items": { - "type": "integer", - "format": "int32" + "type": "string" }, "minItems": 2, "maxItems": 2 @@ -63,20 +49,19 @@ }, "x": { "type": "integer", - "format": "int32" + "format": "int32", + "minimum": -100, + "maximum": 100 } }, "required": [ "min_max", "min_max2", - "regex_str1", - "regex_str2", - "contains_str1", - "contains_str2", + "regex_str", + "contains_str", "email_address", "homepage", "non_empty_str", - "non_empty_str2", "pair", "required_option", "x" diff --git a/schemars/tests/expected/validate.json b/schemars/tests/integration/snapshots/schemars/tests/integration/validator.rs~validate_attrs.ser.json similarity index 61% rename from schemars/tests/expected/validate.json rename to schemars/tests/integration/snapshots/schemars/tests/integration/validator.rs~validate_attrs.ser.json index 64b571f0..f4e1fc44 100644 --- a/schemars/tests/expected/validate.json +++ b/schemars/tests/integration/snapshots/schemars/tests/integration/validator.rs~validate_attrs.ser.json @@ -1,33 +1,25 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "Struct", + "title": "ValidateAttrStruct", "type": "object", "properties": { "min_max": { "type": "number", "format": "float", - "minimum": 0.01, - "maximum": 100 + "minimum": 1.0, + "maximum": 100.0 }, "min_max2": { "type": "number", "format": "float", - "minimum": 1, - "maximum": 1000 - }, - "regex_str1": { - "type": "string", - "pattern": "^[Hh]ello\\b" - }, - "regex_str2": { - "type": "string", - "pattern": "^[Hh]ello\\b" + "minimum": 1.0, + "maximum": 10.0 }, - "contains_str1": { + "regex_str": { "type": "string", - "pattern": "substring\\.\\.\\." + "pattern": "^[Hh]ello" }, - "contains_str2": { + "contains_str": { "type": "string", "pattern": "substring\\.\\.\\." }, @@ -44,16 +36,10 @@ "minLength": 1, "maxLength": 100 }, - "non_empty_str2": { - "type": "string", - "minLength": 1, - "maxLength": 1000 - }, "pair": { "type": "array", "items": { - "type": "integer", - "format": "int32" + "type": "string" }, "minItems": 2, "maxItems": 2 @@ -63,20 +49,19 @@ }, "x": { "type": "integer", - "format": "int32" + "format": "int32", + "minimum": -100, + "maximum": 100 } }, "required": [ "min_max", "min_max2", - "regex_str1", - "regex_str2", - "contains_str1", - "contains_str2", + "regex_str", + "contains_str", "email_address", "homepage", "non_empty_str", - "non_empty_str2", "pair", "required_option", "x" diff --git a/schemars/tests/integration/validator.rs b/schemars/tests/integration/validator.rs new file mode 100644 index 00000000..44a27080 --- /dev/null +++ b/schemars/tests/integration/validator.rs @@ -0,0 +1,135 @@ +use crate::prelude::*; +use regex::Regex; +use validator::Validate; + +const ONE: f32 = 1.0; +const HUNDRED: f32 = 10.0; + +// In real code, this would use something like a LazyLock +fn hello_regex() -> Regex { + Regex::new(r"^[Hh]ello").unwrap() +} + +#[derive(JsonSchema, Deserialize, Serialize, Validate)] +pub struct ValidateAttrStruct { + #[validate(range(min = 1.0, max = 100.0))] + min_max: f32, + #[validate(range(min = ONE, max = HUNDRED))] + min_max2: f32, + #[validate(regex(path = hello_regex()))] + regex_str: String, + #[validate(contains(pattern = "substring..."))] + contains_str: String, + #[validate(email)] + email_address: String, + #[validate(url)] + homepage: String, + #[validate(length(min = 1, max = 100))] + non_empty_str: String, + #[validate(length(equal = 2))] + pair: Vec, + #[validate(required)] + required_option: Option, + #[validate(required, nested)] + #[serde(flatten)] + required_flattened: Option, +} + +#[derive(JsonSchema, Deserialize, Serialize, Validate)] +pub struct ValidateAttrInner { + #[validate(range(min = -100, max = 100))] + x: i32, +} + +impl Default for ValidateAttrStruct { + fn default() -> Self { + Self { + min_max: 1.0, + min_max2: 1.0, + regex_str: "Hello world".to_owned(), + contains_str: "Contains substring...".to_owned(), + email_address: "test@test.test".to_owned(), + homepage: "http://test.test".to_owned(), + non_empty_str: "test".to_owned(), + pair: vec!["a".to_owned(), "b".to_owned()], + required_option: Some(true), + required_flattened: Some(ValidateAttrInner { x: 0 }), + } + } +} + +impl ValidateAttrStruct { + pub fn invalid_values() -> impl IntoIterator { + static MUTATORS: &[fn(&mut ValidateAttrStruct)] = &[ + |v| v.min_max = 0.9, + |v| v.min_max = 100.1, + |v| v.min_max2 = 0.9, + |v| v.min_max2 = 100.1, + |v| v.regex_str = "fail".to_owned(), + |v| v.contains_str = "fail".to_owned(), + |v| v.email_address = "fail".to_owned(), + |v| v.homepage = "fail".to_owned(), + |v| v.non_empty_str = String::new(), + |v| v.pair = Vec::new(), + |v| v.pair = vec!["a".to_owned(), "b".to_owned(), "c".to_owned()], + |v| v.required_option = None, + |v| v.required_flattened = None, + |v| v.required_flattened = Some(ValidateAttrInner { x: -101 }), + |v| v.required_flattened = Some(ValidateAttrInner { x: 101 }), + ]; + MUTATORS.iter().map(|f| { + let mut result = ValidateAttrStruct::default(); + f(&mut result); + result + }) + } +} + +#[test] +fn validate_attrs() { + test!(ValidateAttrStruct) + .with_validator(|v| v.validate().is_ok()) + .assert_snapshot() + .assert_allows_ser_roundtrip_default() + .assert_rejects_invalid(ValidateAttrStruct::invalid_values()) + .assert_matches_de_roundtrip(arbitrary_values()); +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(rename = "ValidateAttrStruct")] +pub struct SchemarsAttrStruct { + #[schemars(range(min = 1.0, max = 100.0))] + min_max: f32, + #[schemars(range(min = ONE, max = HUNDRED))] + min_max2: f32, + #[schemars(regex(pattern = hello_regex()))] + regex_str: String, + #[schemars(contains(pattern = "substring..."))] + contains_str: String, + #[schemars(email)] + email_address: String, + #[schemars(url)] + homepage: String, + #[schemars(length(min = 1, max = 100))] + non_empty_str: String, + #[schemars(length(equal = 2))] + pair: Vec, + #[schemars(required)] + required_option: Option, + #[schemars(required)] + #[serde(flatten)] + required_flattened: Option, +} + +#[allow(dead_code)] +#[derive(JsonSchema)] +pub struct SchemarsAttrInner { + #[schemars(range(min = -100, max = 100))] + x: i32, +} + +#[test] +fn schemars_attrs() { + test!(SchemarsAttrStruct).assert_identical::(); +} diff --git a/schemars/tests/validate.rs b/schemars/tests/validate.rs deleted file mode 100644 index 410593d6..00000000 --- a/schemars/tests/validate.rs +++ /dev/null @@ -1,121 +0,0 @@ -mod util; -use schemars::JsonSchema; -use util::*; - -struct FakeRegex(&'static str); - -impl std::fmt::Display for FakeRegex { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - -// In real code, this would typically be a Regex, potentially created in a `lazy_static!`. -static STARTS_WITH_HELLO: &FakeRegex = &FakeRegex(r"^[Hh]ello\b"); - -const MIN: u32 = 1; -const MAX: u32 = 1000; - -#[allow(dead_code)] -#[derive(JsonSchema)] -pub struct Struct { - #[validate(range(min = 0.01, max = 100))] - min_max: f32, - #[validate(range(min = "MIN", max = "MAX"))] - min_max2: f32, - #[validate(regex(path = *STARTS_WITH_HELLO))] - regex_str1: String, - #[validate(regex(path = "STARTS_WITH_HELLO", code = "foo"))] - regex_str2: String, - #[validate(contains(pattern = "substring..."))] - contains_str1: String, - #[validate(contains(pattern = "substring...", message = "bar"))] - contains_str2: String, - #[validate(email)] - email_address: String, - #[validate(url(code = "code_str", message = "message"))] - homepage: String, - #[validate(length(min = 1, max = 100))] - non_empty_str: String, - #[validate(length(min = "MIN", max = "MAX"))] - non_empty_str2: String, - #[validate(length(equal = 2))] - pair: Vec, - #[validate(required)] - required_option: Option, - #[validate(required)] - #[validate] - #[serde(flatten)] - required_flattened: Option, -} - -#[allow(dead_code)] -#[derive(JsonSchema)] -pub struct Inner { - x: i32, -} - -#[test] -fn validate() -> TestResult { - test_default_generated_schema::("validate") -} - -#[allow(dead_code)] -#[derive(JsonSchema)] -pub struct Struct2 { - #[schemars(range(min = 0.01, max = 100))] - min_max: f32, - #[schemars(range(min = "MIN", max = "MAX"))] - min_max2: f32, - #[validate(regex(path = overridden))] - #[schemars(regex(pattern = *STARTS_WITH_HELLO))] - regex_str1: String, - #[schemars(regex(pattern = r"^\d+$"))] - regex_str2: String, - #[validate(contains(pattern = "overridden"))] - #[schemars(contains(pattern = "substring..."))] - contains_str1: String, - #[schemars(contains(pattern = "substring..."))] - contains_str2: String, - #[schemars(email)] - email_address: String, - #[schemars(url)] - homepage: String, - #[schemars(length(min = 1, max = 100))] - non_empty_str: String, - #[schemars(length(min = "MIN", max = "MAX"))] - non_empty_str2: String, - #[schemars(length(equal = 2))] - pair: Vec, - #[schemars(required)] - required_option: Option, - #[schemars(required)] - #[serde(flatten)] - required_flattened: Option, -} - -#[test] -fn validate_schemars_attrs() -> TestResult { - test_default_generated_schema::("validate_schemars_attrs") -} - -#[allow(dead_code)] -#[derive(JsonSchema)] -pub struct Tuple( - #[validate(range(max = 10))] u8, - #[validate(required)] Option, -); - -#[test] -fn validate_tuple() -> TestResult { - test_default_generated_schema::("validate_tuple") -} - -#[allow(dead_code)] -#[derive(JsonSchema)] -pub struct NewType(#[validate(range(max = 10))] u8); - -#[test] -fn validate_newtype() -> TestResult { - test_default_generated_schema::("validate_newtype") -} diff --git a/schemars/tests/validate_inner.rs b/schemars/tests/validate_inner.rs deleted file mode 100644 index d3d8d652..00000000 --- a/schemars/tests/validate_inner.rs +++ /dev/null @@ -1,31 +0,0 @@ -mod util; - -use schemars::JsonSchema; -use util::*; - -// In real code, this would typically be a Regex, potentially created in a `lazy_static!`. -static STARTS_WITH_HELLO: &str = r"^[Hh]ello\b"; - -#[allow(dead_code)] -#[derive(JsonSchema)] -pub struct Struct<'a> { - #[schemars(inner(length(min = 5, max = 100)))] - array_str_length: [&'a str; 2], - #[schemars(inner(contains(pattern = "substring...")))] - slice_str_contains: &'a [&'a str], - #[schemars(inner(regex(pattern = STARTS_WITH_HELLO)))] - vec_str_regex: Vec, - #[schemars(inner(length(min = 1, max = 100)))] - vec_str_length: Vec<&'a str>, - #[schemars(length(min = 1, max = 3), inner(length(min = 1, max = 100)))] - vec_str_length2: Vec, - #[schemars(inner(url))] - vec_str_url: Vec, - #[schemars(inner(range(min = -10, max = 10)))] - vec_i32_range: Vec, -} - -#[test] -fn validate_inner() -> TestResult { - test_default_generated_schema::("validate_inner") -}