diff --git a/serde_with/src/schemars_0_8.rs b/serde_with/src/schemars_0_8.rs index ec574125..f7b1bad9 100644 --- a/serde_with/src/schemars_0_8.rs +++ b/serde_with/src/schemars_0_8.rs @@ -6,7 +6,7 @@ //! see [`JsonSchemaAs`]. use crate::{ - formats::{Flexible, Format, Separator, Strict}, + formats::{Flexible, Format, PreferMany, PreferOne, Separator, Strict}, prelude::{Schema as WrapSchema, *}, }; use ::schemars_0_8::{ @@ -794,6 +794,99 @@ map_first_last_wins_schema!(=> S indexmap_1::IndexMap); #[cfg(feature = "indexmap_2")] map_first_last_wins_schema!(=> S indexmap_2::IndexMap); +impl JsonSchema for WrapSchema, OneOrMany> +where + WrapSchema: JsonSchema, +{ + fn schema_name() -> String { + std::format!( + "OneOrMany<{}, PreferOne>", + >::schema_name() + ) + } + + fn schema_id() -> Cow<'static, str> { + std::format!( + "serde_with::OneOrMany<{}, PreferOne>", + >::schema_id() + ) + .into() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let single = gen.subschema_for::>(); + let array = SchemaObject { + instance_type: Some(InstanceType::Array.into()), + array: Some(Box::new(ArrayValidation { + items: Some(Schema::from(single.clone()).into()), + ..Default::default() + })), + ..Default::default() + }; + + SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + any_of: Some(std::vec![single, array.into()]), + ..Default::default() + })), + ..Default::default() + } + .into() + } +} + +impl JsonSchema for WrapSchema, OneOrMany> +where + WrapSchema: JsonSchema, +{ + fn schema_name() -> String { + std::format!( + "OneOrMany<{}, PreferMany>", + >::schema_name() + ) + } + + fn schema_id() -> Cow<'static, str> { + std::format!( + "serde_with::OneOrMany<{}, PreferMany>", + >::schema_id() + ) + .into() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let inner = gen.subschema_for::>(); + let single = SchemaObject { + metadata: Some(Box::new(Metadata { + write_only: true, + ..Default::default() + })), + subschemas: Some(Box::new(SubschemaValidation { + all_of: Some(std::vec![inner.clone()]), + ..Default::default() + })), + ..Default::default() + }; + let array = SchemaObject { + instance_type: Some(InstanceType::Array.into()), + array: Some(Box::new(ArrayValidation { + items: Some(Schema::from(single.clone()).into()), + ..Default::default() + })), + ..Default::default() + }; + + SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + any_of: Some(std::vec![single.into(), array.into()]), + ..Default::default() + })), + ..Default::default() + } + .into() + } +} + impl JsonSchemaAs for SetLastValueWins where TA: JsonSchemaAs, diff --git a/serde_with/tests/schemars_0_8.rs b/serde_with/tests/schemars_0_8.rs index 20f17f55..29851bd6 100644 --- a/serde_with/tests/schemars_0_8.rs +++ b/serde_with/tests/schemars_0_8.rs @@ -352,6 +352,14 @@ mod snapshots { data: Vec, } } + + one_or_many_prefer_one { + #[serde(transparent)] + struct Test { + #[serde_as(as = "OneOrMany<_, PreferOne>")] + data: Vec, + } + } } } @@ -870,3 +878,67 @@ mod key_value_map { check_valid_json_schema(&value); } } + +mod one_or_many { + use super::*; + use serde_with::formats::{PreferMany, PreferOne}; + + #[serde_as] + #[derive(Clone, Debug, JsonSchema, Serialize)] + #[serde(transparent)] + struct WithPreferOne(#[serde_as(as = "OneOrMany<_, PreferOne>")] Vec); + + #[serde_as] + #[derive(Clone, Debug, JsonSchema, Serialize)] + #[serde(transparent)] + struct WithPreferMany(#[serde_as(as = "OneOrMany<_, PreferMany>")] Vec); + + #[test] + fn test_prefer_one() { + let single = WithPreferOne(vec![7]); + let multiple = WithPreferOne(vec![1, 2, 3]); + + check_valid_json_schema(&single); + check_valid_json_schema(&multiple); + } + + #[test] + fn test_prefer_one_matches() { + check_matches_schema::(&json!(7)); + check_matches_schema::(&json!([1, 2, 3])); + } + + #[test] + #[should_panic] + fn test_prefer_one_no_invalid_type_one() { + check_matches_schema::(&json!("test")); + } + + #[test] + #[should_panic] + fn test_prefer_one_no_invalid_type_many() { + check_matches_schema::(&json!(["test", 1])); + } + + + #[test] + fn test_prefer_many() { + let single = WithPreferMany(vec![7]); + let multiple = WithPreferMany(vec![1, 2, 3]); + + check_valid_json_schema(&single); + check_valid_json_schema(&multiple); + } + + #[test] + #[should_panic] + fn test_prefer_many_no_invalid_type_one() { + check_matches_schema::(&json!("test")); + } + + #[test] + #[should_panic] + fn test_prefer_many_no_invalid_type_many() { + check_matches_schema::(&json!(["test", 1])); + } +} diff --git a/serde_with/tests/schemars_0_8/snapshots/one_or_many_prefer_one.json b/serde_with/tests/schemars_0_8/snapshots/one_or_many_prefer_one.json new file mode 100644 index 00000000..b9aa247a --- /dev/null +++ b/serde_with/tests/schemars_0_8/snapshots/one_or_many_prefer_one.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "OneOrMany", + "anyOf": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + ] +}