diff --git a/serde_with/src/schemars_0_8.rs b/serde_with/src/schemars_0_8.rs index 8ab0a0c4..1696bd80 100644 --- a/serde_with/src/schemars_0_8.rs +++ b/serde_with/src/schemars_0_8.rs @@ -887,6 +887,79 @@ where } } +macro_rules! schema_for_pickfirst { + ($( $param:ident )+) => { + impl JsonSchemaAs for PickFirst<($( $param, )+)> + where + $( $param: JsonSchemaAs, )+ + { + fn schema_name() -> String { + std::format!( + concat!( + "PickFirst<(", + $( "{", stringify!($param), "}", )+ + ")>" + ), + $( $param = >::schema_name(), )+ + ) + } + + fn schema_id() -> Cow<'static, str> { + std::format!( + concat!( + "serde_with::PickFirst<(", + $( "{", stringify!($param), "}", )+ + ")>" + ), + $( $param = >::schema_id(), )+ + ) + .into() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + let mut first = true; + let subschemas = std::vec![$( + { + let is_first = std::mem::replace(&mut first, false); + let schema = gen.subschema_for::>(); + + if !is_first { + SchemaObject { + metadata: Some(Box::new(Metadata { + write_only: true, + ..Default::default() + })), + subschemas: Some(Box::new(SubschemaValidation { + all_of: Some(std::vec![schema]), + ..Default::default() + })), + ..Default::default() + } + .into() + } else { + schema + } + } + ),+]; + + SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + any_of: Some(subschemas), + ..Default::default() + })), + ..Default::default() + } + .into() + } + } + } +} + +schema_for_pickfirst!(A); +schema_for_pickfirst!(A B); +schema_for_pickfirst!(A B C); +schema_for_pickfirst!(A B C D); + 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 b9d3f30e..03e2c4b1 100644 --- a/serde_with/tests/schemars_0_8.rs +++ b/serde_with/tests/schemars_0_8.rs @@ -360,6 +360,14 @@ mod snapshots { data: Vec, } } + + pickfirst { + #[serde(transparent)] + struct Test { + #[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] + value: u32 + } + } } } @@ -942,3 +950,14 @@ mod one_or_many { check_matches_schema::(&json!(["test", 1])); } } + +#[test] +fn test_pickfirst() { + #[serde_as] + #[derive(JsonSchema, Serialize)] + #[serde(transparent)] + struct IntOrDisplay(#[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] u32); + + check_matches_schema::(&json!(7)); + check_matches_schema::(&json!("17")); +} diff --git a/serde_with/tests/schemars_0_8/snapshots/pickfirst.json b/serde_with/tests/schemars_0_8/snapshots/pickfirst.json new file mode 100644 index 00000000..766bcaef --- /dev/null +++ b/serde_with/tests/schemars_0_8/snapshots/pickfirst.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PickFirst<(uint32String)>", + "anyOf": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "writeOnly": true, + "allOf": [ + { + "type": "string" + } + ] + } + ] +}