tamasfe_schemars/
schema.rs

1/*!
2JSON Schema types.
3*/
4
5#[cfg(feature = "impl_json_schema")]
6use crate as schemars;
7#[cfg(feature = "impl_json_schema")]
8use crate::JsonSchema;
9use crate::{Map, Set};
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13/// A JSON Schema.
14#[allow(clippy::large_enum_variant)]
15#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
16#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
17#[serde(untagged)]
18pub enum Schema {
19    /// A trivial boolean JSON Schema.
20    ///
21    /// The schema `true` matches everything (always passes validation), whereas the schema `false`
22    /// matches nothing (always fails validation).
23    Bool(bool),
24    /// A JSON Schema object.
25    Object(SchemaObject),
26}
27
28impl Schema {
29    /// Creates a new `$ref` schema.
30    ///
31    /// The given reference string should be a URI reference. This will usually be a JSON Pointer
32    /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6).
33    pub fn new_ref(reference: String) -> Self {
34        SchemaObject::new_ref(reference).into()
35    }
36
37    /// Returns `true` if `self` is a `$ref` schema.
38    ///
39    /// If `self` is a [`SchemaObject`] with `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
40    /// Otherwise, returns `false`.
41    pub fn is_ref(&self) -> bool {
42        match self {
43            Schema::Object(o) => o.is_ref(),
44            _ => false,
45        }
46    }
47
48    /// Converts the given schema (if it is a boolean schema) into an equivalent schema object.
49    ///
50    /// If the given schema is already a schema object, this has no effect.
51    ///
52    /// # Example
53    /// ```
54    /// use schemars::schema::{Schema, SchemaObject};
55    ///
56    /// let bool_schema = Schema::Bool(true);
57    ///
58    /// assert_eq!(bool_schema.into_object(), SchemaObject::default());
59    /// ```
60    pub fn into_object(self) -> SchemaObject {
61        match self {
62            Schema::Object(o) => o,
63            Schema::Bool(true) => SchemaObject::default(),
64            Schema::Bool(false) => SchemaObject {
65                subschemas: Some(Box::new(SubschemaValidation {
66                    not: Some(Schema::Object(Default::default()).into()),
67                    ..Default::default()
68                })),
69                ..Default::default()
70            },
71        }
72    }
73}
74
75impl From<SchemaObject> for Schema {
76    fn from(o: SchemaObject) -> Self {
77        Schema::Object(o)
78    }
79}
80
81impl From<bool> for Schema {
82    fn from(b: bool) -> Self {
83        Schema::Bool(b)
84    }
85}
86
87/// The root object of a JSON Schema document.
88#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
89#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
90#[serde(rename_all = "camelCase", default)]
91pub struct RootSchema {
92    /// The `$schema` keyword.
93    ///
94    /// See [JSON Schema 8.1.1. The "$schema" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).
95    #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
96    pub meta_schema: Option<String>,
97    /// The root schema itself.
98    #[serde(flatten)]
99    pub schema: SchemaObject,
100    /// The `definitions` keyword.
101    ///
102    /// In JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still
103    /// serialized as `definitions` for backward-compatibility.
104    ///
105    /// See [JSON Schema 8.2.5. Schema Re-Use With "$defs"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5),
106    /// and [JSON Schema (draft 07) 9. Schema Re-Use With "definitions"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9).
107    #[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
108    pub definitions: Map<String, Schema>,
109}
110
111/// A JSON Schema object.
112#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
113#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
114#[serde(rename_all = "camelCase", default)]
115pub struct SchemaObject {
116    /// Properties which annotate the [`SchemaObject`] which typically have no effect when an object is being validated against the schema.
117    #[serde(flatten, deserialize_with = "skip_if_default")]
118    pub metadata: Option<Box<Metadata>>,
119    /// The `type` keyword.
120    ///
121    /// See [JSON Schema Validation 6.1.1. "type"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1)
122    /// and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).
123    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
124    pub instance_type: Option<SingleOrVec<InstanceType>>,
125    /// The `format` keyword.
126    ///
127    /// See [JSON Schema Validation 7. A Vocabulary for Semantic Content With "format"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub format: Option<String>,
130    /// The `enum` keyword.
131    ///
132    /// See [JSON Schema Validation 6.1.2. "enum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)
133    #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
134    pub enum_values: Option<Vec<Value>>,
135    /// The `const` keyword.
136    ///
137    /// See [JSON Schema Validation 6.1.3. "const"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)
138    #[serde(
139        rename = "const",
140        skip_serializing_if = "Option::is_none",
141        deserialize_with = "allow_null"
142    )]
143    pub const_value: Option<Value>,
144    /// Properties of the [`SchemaObject`] which define validation assertions in terms of other schemas.
145    #[serde(flatten, deserialize_with = "skip_if_default")]
146    pub subschemas: Option<Box<SubschemaValidation>>,
147    /// Properties of the [`SchemaObject`] which define validation assertions for numbers.
148    #[serde(flatten, deserialize_with = "skip_if_default")]
149    pub number: Option<Box<NumberValidation>>,
150    /// Properties of the [`SchemaObject`] which define validation assertions for strings.
151    #[serde(flatten, deserialize_with = "skip_if_default")]
152    pub string: Option<Box<StringValidation>>,
153    /// Properties of the [`SchemaObject`] which define validation assertions for arrays.
154    #[serde(flatten, deserialize_with = "skip_if_default")]
155    pub array: Option<Box<ArrayValidation>>,
156    /// Properties of the [`SchemaObject`] which define validation assertions for objects.
157    #[serde(flatten, deserialize_with = "skip_if_default")]
158    pub object: Option<Box<ObjectValidation>>,
159    /// The `$ref` keyword.
160    ///
161    /// See [JSON Schema 8.2.4.1. Direct References with "$ref"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).
162    #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
163    pub reference: Option<String>,
164    /// Arbitrary extra properties which are not part of the JSON Schema specification, or which `schemars` does not support.
165    #[serde(flatten)]
166    pub extensions: Map<String, Value>,
167}
168
169// Deserializing "null" to `Option<Value>` directly results in `None`,
170// this function instead makes it deserialize to `Some(Value::Null)`.
171fn allow_null<'de, D>(de: D) -> Result<Option<Value>, D::Error>
172where
173    D: serde::Deserializer<'de>,
174{
175    Value::deserialize(de).map(Option::Some)
176}
177
178fn skip_if_default<'de, D, T>(deserializer: D) -> Result<Option<Box<T>>, D::Error>
179where
180    D: serde::Deserializer<'de>,
181    T: Deserialize<'de> + Default + PartialEq,
182{
183    let value = T::deserialize(deserializer)?;
184    if value == T::default() {
185        Ok(None)
186    } else {
187        Ok(Some(Box::new(value)))
188    }
189}
190
191macro_rules! get_or_insert_default_fn {
192    ($name:ident, $ret:ty) => {
193        get_or_insert_default_fn!(
194            concat!("Returns a mutable reference to this schema's [`", stringify!($ret), "`](#structfield.", stringify!($name), "), creating it if it was `None`."),
195            $name,
196            $ret
197        );
198    };
199    ($doc:expr, $name:ident, $ret:ty) => {
200        #[doc = $doc]
201        pub fn $name(&mut self) -> &mut $ret {
202            self.$name.get_or_insert_with(Default::default)
203        }
204    };
205}
206
207impl SchemaObject {
208    /// Creates a new `$ref` schema.
209    ///
210    /// The given reference string should be a URI reference. This will usually be a JSON Pointer
211    /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6).
212    pub fn new_ref(reference: String) -> Self {
213        SchemaObject {
214            reference: Some(reference),
215            ..Default::default()
216        }
217    }
218
219    /// Returns `true` if `self` is a `$ref` schema.
220    ///
221    /// If `self` has `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
222    /// Otherwise, returns `false`.
223    pub fn is_ref(&self) -> bool {
224        self.reference.is_some()
225    }
226
227    get_or_insert_default_fn!(metadata, Metadata);
228    get_or_insert_default_fn!(subschemas, SubschemaValidation);
229    get_or_insert_default_fn!(number, NumberValidation);
230    get_or_insert_default_fn!(string, StringValidation);
231    get_or_insert_default_fn!(array, ArrayValidation);
232    get_or_insert_default_fn!(object, ObjectValidation);
233}
234
235impl From<Schema> for SchemaObject {
236    fn from(schema: Schema) -> Self {
237        schema.into_object()
238    }
239}
240
241/// Properties which annotate a [`SchemaObject`] which typically have no effect when an object is being validated against the schema.
242#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
243#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
244#[serde(rename_all = "camelCase", default)]
245pub struct Metadata {
246    /// The `$id` keyword.
247    ///
248    /// See [JSON Schema 8.2.2. The "$id" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).
249    #[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
250    pub id: Option<String>,
251    /// The `title` keyword.
252    ///
253    /// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub title: Option<String>,
256    /// The `description` keyword.
257    ///
258    /// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub description: Option<String>,
261    /// The `default` keyword.
262    ///
263    /// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).
264    #[serde(
265        skip_serializing_if = "Option::is_none",
266        deserialize_with = "allow_null"
267    )]
268    pub default: Option<Value>,
269    /// The `deprecated` keyword.
270    ///
271    /// See [JSON Schema Validation 9.3. "deprecated"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).
272    #[serde(skip_serializing_if = "is_false")]
273    pub deprecated: bool,
274    /// The `readOnly` keyword.
275    ///
276    /// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).
277    #[serde(skip_serializing_if = "is_false")]
278    pub read_only: bool,
279    /// The `writeOnly` keyword.
280    ///
281    /// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).
282    #[serde(skip_serializing_if = "is_false")]
283    pub write_only: bool,
284    /// The `examples` keyword.
285    ///
286    /// See [JSON Schema Validation 9.5. "examples"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).
287    #[serde(skip_serializing_if = "Vec::is_empty")]
288    pub examples: Vec<Value>,
289}
290
291#[allow(clippy::trivially_copy_pass_by_ref)]
292fn is_false(b: &bool) -> bool {
293    !b
294}
295
296/// Properties of a [`SchemaObject`] which define validation assertions in terms of other schemas.
297#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
298#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
299#[serde(rename_all = "camelCase", default)]
300pub struct SubschemaValidation {
301    /// The `allOf` keyword.
302    ///
303    /// See [JSON Schema 9.2.1.1. "allOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub all_of: Option<Vec<Schema>>,
306    /// The `anyOf` keyword.
307    ///
308    /// See [JSON Schema 9.2.1.2. "anyOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub any_of: Option<Vec<Schema>>,
311    /// The `oneOf` keyword.
312    ///
313    /// See [JSON Schema 9.2.1.3. "oneOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub one_of: Option<Vec<Schema>>,
316    /// The `not` keyword.
317    ///
318    /// See [JSON Schema 9.2.1.4. "not"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub not: Option<Box<Schema>>,
321    /// The `if` keyword.
322    ///
323    /// See [JSON Schema 9.2.2.1. "if"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).
324    #[serde(rename = "if", skip_serializing_if = "Option::is_none")]
325    pub if_schema: Option<Box<Schema>>,
326    /// The `then` keyword.
327    ///
328    /// See [JSON Schema 9.2.2.2. "then"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).
329    #[serde(rename = "then", skip_serializing_if = "Option::is_none")]
330    pub then_schema: Option<Box<Schema>>,
331    /// The `else` keyword.
332    ///
333    /// See [JSON Schema 9.2.2.3. "else"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).
334    #[serde(rename = "else", skip_serializing_if = "Option::is_none")]
335    pub else_schema: Option<Box<Schema>>,
336}
337
338/// Properties of a [`SchemaObject`] which define validation assertions for numbers.
339#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
340#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
341#[serde(rename_all = "camelCase", default)]
342pub struct NumberValidation {
343    /// The `multipleOf` keyword.
344    ///
345    /// See [JSON Schema Validation 6.2.1. "multipleOf"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub multiple_of: Option<f64>,
348    /// The `maximum` keyword.
349    ///
350    /// See [JSON Schema Validation 6.2.2. "maximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).
351    #[serde(skip_serializing_if = "Option::is_none")]
352    pub maximum: Option<f64>,
353    /// The `exclusiveMaximum` keyword.
354    ///
355    /// See [JSON Schema Validation 6.2.3. "exclusiveMaximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).
356    #[serde(skip_serializing_if = "Option::is_none")]
357    pub exclusive_maximum: Option<f64>,
358    /// The `minimum` keyword.
359    ///
360    /// See [JSON Schema Validation 6.2.4. "minimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub minimum: Option<f64>,
363    /// The `exclusiveMinimum` keyword.
364    ///
365    /// See [JSON Schema Validation 6.2.5. "exclusiveMinimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub exclusive_minimum: Option<f64>,
368}
369
370/// Properties of a [`SchemaObject`] which define validation assertions for strings.
371#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
372#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
373#[serde(rename_all = "camelCase", default)]
374pub struct StringValidation {
375    /// The `maxLength` keyword.
376    ///
377    /// See [JSON Schema Validation 6.3.1. "maxLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub max_length: Option<u32>,
380    /// The `minLength` keyword.
381    ///
382    /// See [JSON Schema Validation 6.3.2. "minLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).
383    #[serde(skip_serializing_if = "Option::is_none")]
384    pub min_length: Option<u32>,
385    /// The `pattern` keyword.
386    ///
387    /// See [JSON Schema Validation 6.3.3. "pattern"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).
388    #[serde(skip_serializing_if = "Option::is_none")]
389    pub pattern: Option<String>,
390}
391
392/// Properties of a [`SchemaObject`] which define validation assertions for arrays.
393#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
394#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
395#[serde(rename_all = "camelCase", default)]
396pub struct ArrayValidation {
397    /// The `items` keyword.
398    ///
399    /// See [JSON Schema 9.3.1.1. "items"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).
400    #[serde(skip_serializing_if = "Option::is_none")]
401    pub items: Option<SingleOrVec<Schema>>,
402    /// The `additionalItems` keyword.
403    ///
404    /// See [JSON Schema 9.3.1.2. "additionalItems"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).
405    #[serde(skip_serializing_if = "Option::is_none")]
406    pub additional_items: Option<Box<Schema>>,
407    /// The `maxItems` keyword.
408    ///
409    /// See [JSON Schema Validation 6.4.1. "maxItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub max_items: Option<u32>,
412    /// The `minItems` keyword.
413    ///
414    /// See [JSON Schema Validation 6.4.2. "minItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).
415    #[serde(skip_serializing_if = "Option::is_none")]
416    pub min_items: Option<u32>,
417    /// The `uniqueItems` keyword.
418    ///
419    /// See [JSON Schema Validation 6.4.3. "uniqueItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).
420    #[serde(skip_serializing_if = "Option::is_none")]
421    pub unique_items: Option<bool>,
422    /// The `contains` keyword.
423    ///
424    /// See [JSON Schema 9.3.1.4. "contains"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).
425    #[serde(skip_serializing_if = "Option::is_none")]
426    pub contains: Option<Box<Schema>>,
427}
428
429/// Properties of a [`SchemaObject`] which define validation assertions for objects.
430#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
431#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
432#[serde(rename_all = "camelCase", default)]
433pub struct ObjectValidation {
434    /// The `maxProperties` keyword.
435    ///
436    /// See [JSON Schema Validation 6.5.1. "maxProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub max_properties: Option<u32>,
439    /// The `minProperties` keyword.
440    ///
441    /// See [JSON Schema Validation 6.5.2. "minProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub min_properties: Option<u32>,
444    /// The `required` keyword.
445    ///
446    /// See [JSON Schema Validation 6.5.3. "required"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).
447    #[serde(skip_serializing_if = "Set::is_empty")]
448    pub required: Set<String>,
449    /// The `properties` keyword.
450    ///
451    /// See [JSON Schema 9.3.2.1. "properties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).
452    #[serde(skip_serializing_if = "Map::is_empty")]
453    pub properties: Map<String, Schema>,
454    /// The `patternProperties` keyword.
455    ///
456    /// See [JSON Schema 9.3.2.2. "patternProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).
457    #[serde(skip_serializing_if = "Map::is_empty")]
458    pub pattern_properties: Map<String, Schema>,
459    /// The `additionalProperties` keyword.
460    ///
461    /// See [JSON Schema 9.3.2.3. "additionalProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).
462    #[serde(skip_serializing_if = "Option::is_none")]
463    pub additional_properties: Option<Box<Schema>>,
464    /// The `propertyNames` keyword.
465    ///
466    /// See [JSON Schema 9.3.2.5. "propertyNames"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).
467    #[serde(skip_serializing_if = "Option::is_none")]
468    pub property_names: Option<Box<Schema>>,
469}
470
471/// The possible types of values in JSON Schema documents.
472///
473/// See [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).
474#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
475#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
476#[serde(rename_all = "camelCase")]
477pub enum InstanceType {
478    Null,
479    Boolean,
480    Object,
481    Array,
482    Number,
483    String,
484    Integer,
485}
486
487/// A type which can be serialized as a single item, or multiple items.
488///
489/// In some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.
490#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
491#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
492#[serde(untagged)]
493pub enum SingleOrVec<T> {
494    Single(Box<T>),
495    Vec(Vec<T>),
496}
497
498impl<T> From<T> for SingleOrVec<T> {
499    fn from(single: T) -> Self {
500        SingleOrVec::Single(Box::new(single))
501    }
502}
503
504impl<T> From<Vec<T>> for SingleOrVec<T> {
505    fn from(vec: Vec<T>) -> Self {
506        SingleOrVec::Vec(vec)
507    }
508}