Skip to content

Commit 170b6e1

Browse files
authored
Merge pull request #676 from swlynch99/schemars-fwd
2 parents d2fc3a4 + 78d771d commit 170b6e1

17 files changed

+433
-10
lines changed

serde_with/src/schemars_0_8.rs

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
//!
33
//! This module is only available if using the `schemars_0_8` feature of the crate.
44
5-
use crate::prelude::{Schema as WrapSchema, *};
5+
use crate::{
6+
formats::Separator,
7+
prelude::{Schema as WrapSchema, *},
8+
};
69
use ::schemars_0_8::{
710
gen::SchemaGenerator,
811
schema::{ArrayValidation, InstanceType, Schema, SchemaObject},
@@ -222,3 +225,163 @@ impl<T: JsonSchema> JsonSchema for WrapSchema<T, Same> {
222225
impl<T> JsonSchema for WrapSchema<T, DisplayFromStr> {
223226
forward_schema!(String);
224227
}
228+
229+
impl<'a, T: 'a> JsonSchema for WrapSchema<Cow<'a, T>, BorrowCow>
230+
where
231+
T: ?Sized + ToOwned,
232+
Cow<'a, T>: JsonSchema,
233+
{
234+
forward_schema!(Cow<'a, T>);
235+
}
236+
237+
impl<T> JsonSchema for WrapSchema<T, Bytes> {
238+
forward_schema!(Vec<u8>);
239+
}
240+
241+
impl<T, TA> JsonSchema for WrapSchema<T, DefaultOnError<TA>>
242+
where
243+
WrapSchema<T, TA>: JsonSchema,
244+
{
245+
forward_schema!(WrapSchema<T, TA>);
246+
}
247+
248+
impl<T, TA> JsonSchema for WrapSchema<T, DefaultOnNull<TA>>
249+
where
250+
WrapSchema<T, TA>: JsonSchema,
251+
{
252+
forward_schema!(Option<WrapSchema<T, TA>>);
253+
}
254+
255+
impl<O, T: JsonSchema> JsonSchema for WrapSchema<O, FromInto<T>> {
256+
forward_schema!(T);
257+
}
258+
259+
impl<O, T: JsonSchema> JsonSchema for WrapSchema<O, FromIntoRef<T>> {
260+
forward_schema!(T);
261+
}
262+
263+
impl<T, U: JsonSchema> JsonSchema for WrapSchema<T, TryFromInto<U>> {
264+
forward_schema!(U);
265+
}
266+
267+
impl<T, U: JsonSchema> JsonSchema for WrapSchema<T, TryFromIntoRef<U>> {
268+
forward_schema!(U);
269+
}
270+
271+
macro_rules! schema_for_map {
272+
($type:ty) => {
273+
impl<K, V, KA, VA> JsonSchema for WrapSchema<$type, Map<KA, VA>>
274+
where
275+
WrapSchema<V, VA>: JsonSchema,
276+
{
277+
forward_schema!(WrapSchema<BTreeMap<K, V>, BTreeMap<KA, VA>>);
278+
}
279+
};
280+
}
281+
282+
schema_for_map!([(K, V)]);
283+
schema_for_map!(BTreeSet<(K, V)>);
284+
schema_for_map!(BinaryHeap<(K, V)>);
285+
schema_for_map!(Box<[(K, V)]>);
286+
schema_for_map!(LinkedList<(K, V)>);
287+
schema_for_map!(Vec<(K, V)>);
288+
schema_for_map!(VecDeque<(K, V)>);
289+
290+
impl<K, V, S, KA, VA> JsonSchema for WrapSchema<HashSet<(K, V), S>, Map<KA, VA>>
291+
where
292+
WrapSchema<V, VA>: JsonSchema,
293+
{
294+
forward_schema!(WrapSchema<BTreeMap<K, V>, BTreeMap<KA, VA>>);
295+
}
296+
297+
impl<K, V, KA, VA, const N: usize> JsonSchema for WrapSchema<[(K, V); N], Map<KA, VA>>
298+
where
299+
WrapSchema<V, VA>: JsonSchema,
300+
{
301+
forward_schema!(WrapSchema<BTreeMap<K, V>, BTreeMap<KA, VA>>);
302+
}
303+
304+
macro_rules! map_first_last_wins_schema {
305+
($(=> $extra:ident)? $type:ty) => {
306+
impl<K, V, $($extra,)? KA, VA> JsonSchema for WrapSchema<$type, MapFirstKeyWins<KA, VA>>
307+
where
308+
WrapSchema<V, VA>: JsonSchema
309+
{
310+
forward_schema!(BTreeMap<WrapSchema<K, KA>, WrapSchema<V, VA>>);
311+
}
312+
313+
impl<K, V, $($extra,)? KA, VA> JsonSchema for WrapSchema<$type, MapPreventDuplicates<KA, VA>>
314+
where
315+
WrapSchema<V, VA>: JsonSchema
316+
{
317+
forward_schema!(BTreeMap<WrapSchema<K, KA>, WrapSchema<V, VA>>);
318+
}
319+
}
320+
}
321+
322+
map_first_last_wins_schema!(BTreeMap<K, V>);
323+
map_first_last_wins_schema!(=> S HashMap<K, V, S>);
324+
#[cfg(feature = "hashbrown_0_14")]
325+
map_first_last_wins_schema!(=> S hashbrown_0_14::HashMap<K, V, S>);
326+
#[cfg(feature = "indexmap_1")]
327+
map_first_last_wins_schema!(=> S indexmap_1::IndexMap<K, V, S>);
328+
#[cfg(feature = "indexmap_2")]
329+
map_first_last_wins_schema!(=> S indexmap_2::IndexMap<K, V, S>);
330+
331+
impl<T, TA> JsonSchema for WrapSchema<T, SetLastValueWins<TA>>
332+
where
333+
WrapSchema<T, TA>: JsonSchema,
334+
{
335+
fn schema_id() -> Cow<'static, str> {
336+
std::format!(
337+
"serde_with::SetLastValueWins<{}>",
338+
<WrapSchema<T, TA> as JsonSchema>::schema_id()
339+
)
340+
.into()
341+
}
342+
343+
fn schema_name() -> String {
344+
std::format!(
345+
"SetLastValueWins<{}>",
346+
<WrapSchema<T, TA> as JsonSchema>::schema_name()
347+
)
348+
}
349+
350+
fn json_schema(gen: &mut ::schemars_0_8::gen::SchemaGenerator) -> Schema {
351+
let schema = <WrapSchema<T, TA> as JsonSchema>::json_schema(gen);
352+
let mut schema = schema.into_object();
353+
354+
// We explicitly allow duplicate items since the whole point of
355+
// SetLastValueWins is to take the duplicate value.
356+
if let Some(array) = &mut schema.array {
357+
array.unique_items = None;
358+
}
359+
360+
schema.into()
361+
}
362+
363+
fn is_referenceable() -> bool {
364+
false
365+
}
366+
}
367+
368+
impl<T, TA> JsonSchema for WrapSchema<T, SetPreventDuplicates<TA>>
369+
where
370+
WrapSchema<T, TA>: JsonSchema,
371+
{
372+
forward_schema!(WrapSchema<T, TA>);
373+
}
374+
375+
impl<SEP, T, TA> JsonSchema for WrapSchema<T, StringWithSeparator<SEP, TA>>
376+
where
377+
SEP: Separator,
378+
{
379+
forward_schema!(String);
380+
}
381+
382+
impl<T, TA> JsonSchema for WrapSchema<Vec<T>, VecSkipError<TA>>
383+
where
384+
WrapSchema<T, TA>: JsonSchema,
385+
{
386+
forward_schema!(Vec<WrapSchema<T, TA>>);
387+
}

serde_with/tests/schemars_0_8.rs

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use expect_test::expect_file;
44
use serde::Serialize;
55
use serde_json::json;
66
use serde_with::*;
7+
use std::collections::BTreeSet;
78

89
// This avoids us having to add `#[schemars(crate = "::schemars_0_8")]` all
910
// over the place. We're not testing that and it is inconvenient.
@@ -43,8 +44,9 @@ macro_rules! declare_snapshot_test {
4344
}
4445

4546
let schema = schemars::schema_for!($name);
46-
let schema = serde_json::to_string_pretty(&schema)
47+
let mut schema = serde_json::to_string_pretty(&schema)
4748
.expect("schema could not be serialized");
49+
schema.push('\n');
4850

4951
let filename = concat!("./", module_path!(), "::", stringify!($test), ".json")
5052
.replace("::", "/");
@@ -86,7 +88,8 @@ fn schemars_basic() {
8688
}
8789

8890
let schema = schemars::schema_for!(Basic);
89-
let schema = serde_json::to_string_pretty(&schema).expect("schema could not be serialized");
91+
let mut schema = serde_json::to_string_pretty(&schema).expect("schema could not be serialized");
92+
schema.push('\n');
9093

9194
let expected = expect_file!["./schemars_0_8/schemars_basic.json"];
9295
expected.assert_eq(&schema);
@@ -150,6 +153,70 @@ mod test_std {
150153
}
151154
}
152155

156+
mod snapshots {
157+
use super::*;
158+
use serde_with::formats::CommaSeparator;
159+
use std::collections::BTreeSet;
160+
161+
declare_snapshot_test! {
162+
bytes {
163+
struct Test {
164+
#[serde_as(as = "Bytes")]
165+
bytes: Vec<u8>,
166+
}
167+
}
168+
169+
default_on_null {
170+
struct Test {
171+
#[serde_as(as = "DefaultOnNull<_>")]
172+
data: String,
173+
}
174+
}
175+
176+
string_with_separator {
177+
struct Test {
178+
#[serde_as(as = "StringWithSeparator<CommaSeparator, String>")]
179+
data: Vec<String>,
180+
}
181+
}
182+
183+
from_into {
184+
struct Test {
185+
#[serde_as(as = "FromInto<u64>")]
186+
data: u32,
187+
}
188+
}
189+
190+
map {
191+
struct Test {
192+
#[serde_as(as = "Map<_, _>")]
193+
data: Vec<(String, u32)>,
194+
}
195+
}
196+
197+
map_fixed {
198+
struct Test {
199+
#[serde_as(as = "Map<_, _>")]
200+
data: [(String, u32); 4],
201+
}
202+
}
203+
204+
set_last_value_wins {
205+
struct Test {
206+
#[serde_as(as = "SetLastValueWins<_>")]
207+
data: BTreeSet<u32>,
208+
}
209+
}
210+
211+
set_prevent_duplicates {
212+
struct Test {
213+
#[serde_as(as = "SetPreventDuplicates<_>")]
214+
data: BTreeSet<u32>,
215+
}
216+
}
217+
}
218+
}
219+
153220
mod derive {
154221
use super::*;
155222

@@ -232,3 +299,61 @@ mod array {
232299
}))
233300
}
234301
}
302+
303+
#[test]
304+
fn test_borrow_cow() {
305+
use std::borrow::Cow;
306+
307+
#[serde_as]
308+
#[derive(Serialize, JsonSchema)]
309+
struct Borrowed<'a> {
310+
#[serde_as(as = "BorrowCow")]
311+
data: Cow<'a, str>,
312+
}
313+
314+
check_valid_json_schema(&Borrowed {
315+
data: Cow::Borrowed("test"),
316+
});
317+
}
318+
319+
#[test]
320+
fn test_map() {
321+
#[serde_as]
322+
#[derive(Serialize, JsonSchema)]
323+
struct Test {
324+
map: [(&'static str, u32); 2],
325+
}
326+
327+
check_valid_json_schema(&Test {
328+
map: [("a", 1), ("b", 2)],
329+
});
330+
}
331+
332+
#[test]
333+
fn test_set_last_value_wins_with_duplicates() {
334+
#[serde_as]
335+
#[derive(Serialize, JsonSchema)]
336+
struct Test {
337+
#[serde_as(as = "SetLastValueWins<_>")]
338+
set: BTreeSet<u32>,
339+
}
340+
341+
check_matches_schema::<Test>(&json!({
342+
"set": [ 1, 2, 3, 1, 4, 2 ]
343+
}));
344+
}
345+
346+
#[test]
347+
#[should_panic]
348+
fn test_set_prevent_duplicates_with_duplicates() {
349+
#[serde_as]
350+
#[derive(Serialize, JsonSchema)]
351+
struct Test {
352+
#[serde_as(as = "SetPreventDuplicates<_>")]
353+
set: BTreeSet<u32>,
354+
}
355+
356+
check_matches_schema::<Test>(&json!({
357+
"set": [ 1, 1 ]
358+
}));
359+
}

serde_with/tests/schemars_0_8/schemars_basic.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@
4242
}
4343
}
4444
}
45-
}
45+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Test",
4+
"type": "object",
5+
"required": [
6+
"bytes"
7+
],
8+
"properties": {
9+
"bytes": {
10+
"type": "array",
11+
"items": {
12+
"type": "integer",
13+
"format": "uint8",
14+
"minimum": 0.0
15+
}
16+
}
17+
}
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Test",
4+
"type": "object",
5+
"required": [
6+
"data"
7+
],
8+
"properties": {
9+
"data": {
10+
"type": [
11+
"string",
12+
"null"
13+
]
14+
}
15+
}
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Test",
4+
"type": "object",
5+
"required": [
6+
"data"
7+
],
8+
"properties": {
9+
"data": {
10+
"type": "integer",
11+
"format": "uint64",
12+
"minimum": 0.0
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)