Skip to content

Commit

Permalink
Implement JsonSchemaAs for OneOrMany (#719)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasbb committed Mar 11, 2024
2 parents c5c35db + 5e36083 commit 7e9772f
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 1 deletion.
95 changes: 94 additions & 1 deletion serde_with/src/schemars_0_8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -794,6 +794,99 @@ map_first_last_wins_schema!(=> S indexmap_1::IndexMap<K, V, S>);
#[cfg(feature = "indexmap_2")]
map_first_last_wins_schema!(=> S indexmap_2::IndexMap<K, V, S>);

impl<T, TA> JsonSchema for WrapSchema<Vec<T>, OneOrMany<TA, PreferOne>>
where
WrapSchema<T, TA>: JsonSchema,
{
fn schema_name() -> String {
std::format!(
"OneOrMany<{}, PreferOne>",
<WrapSchema<T, TA>>::schema_name()
)
}

fn schema_id() -> Cow<'static, str> {
std::format!(
"serde_with::OneOrMany<{}, PreferOne>",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}

fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let single = gen.subschema_for::<WrapSchema<T, TA>>();
let array = SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(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<T, TA> JsonSchema for WrapSchema<Vec<T>, OneOrMany<TA, PreferMany>>
where
WrapSchema<T, TA>: JsonSchema,
{
fn schema_name() -> String {
std::format!(
"OneOrMany<{}, PreferMany>",
<WrapSchema<T, TA>>::schema_name()
)
}

fn schema_id() -> Cow<'static, str> {
std::format!(
"serde_with::OneOrMany<{}, PreferMany>",
<WrapSchema<T, TA>>::schema_id()
)
.into()
}

fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let inner = gen.subschema_for::<WrapSchema<T, TA>>();
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<T, TA> JsonSchemaAs<T> for SetLastValueWins<TA>
where
TA: JsonSchemaAs<T>,
Expand Down
71 changes: 71 additions & 0 deletions serde_with/tests/schemars_0_8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,14 @@ mod snapshots {
data: Vec<KvMapFlatten>,
}
}

one_or_many_prefer_one {
#[serde(transparent)]
struct Test {
#[serde_as(as = "OneOrMany<_, PreferOne>")]
data: Vec<i32>,
}
}
}
}

Expand Down Expand Up @@ -870,3 +878,66 @@ 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<i32>);

#[serde_as]
#[derive(Clone, Debug, JsonSchema, Serialize)]
#[serde(transparent)]
struct WithPreferMany(#[serde_as(as = "OneOrMany<_, PreferMany>")] Vec<i32>);

#[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::<WithPreferOne>(&json!(7));
check_matches_schema::<WithPreferOne>(&json!([1, 2, 3]));
}

#[test]
#[should_panic]
fn test_prefer_one_no_invalid_type_one() {
check_matches_schema::<WithPreferOne>(&json!("test"));
}

#[test]
#[should_panic]
fn test_prefer_one_no_invalid_type_many() {
check_matches_schema::<WithPreferOne>(&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::<WithPreferMany>(&json!("test"));
}

#[test]
#[should_panic]
fn test_prefer_many_no_invalid_type_many() {
check_matches_schema::<WithPreferMany>(&json!(["test", 1]));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "OneOrMany<int32, PreferOne>",
"anyOf": [
{
"type": "integer",
"format": "int32"
},
{
"type": "array",
"items": {
"type": "integer",
"format": "int32"
}
}
]
}

0 comments on commit 7e9772f

Please sign in to comment.