openapiv3/
schema.rs

1use std::str::FromStr;
2
3use crate::*;
4use indexmap::IndexMap;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
8#[serde(rename_all = "camelCase")]
9pub struct SchemaData {
10    #[serde(default, skip_serializing_if = "is_false")]
11    pub nullable: bool,
12    #[serde(default, skip_serializing_if = "is_false")]
13    pub read_only: bool,
14    #[serde(default, skip_serializing_if = "is_false")]
15    pub write_only: bool,
16    #[serde(default, skip_serializing_if = "is_false")]
17    pub deprecated: bool,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub external_docs: Option<ExternalDocumentation>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub example: Option<serde_json::Value>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub title: Option<String>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub description: Option<String>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub discriminator: Option<Discriminator>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub default: Option<serde_json::Value>,
30    /// Inline extensions to this object.
31    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
32    pub extensions: IndexMap<String, serde_json::Value>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
36pub struct Schema {
37    #[serde(flatten)]
38    pub schema_data: SchemaData,
39    #[serde(flatten)]
40    pub schema_kind: SchemaKind,
41}
42
43#[derive(Debug, Clone, Serialize, PartialEq)]
44#[serde(untagged)]
45pub enum SchemaKind {
46    Type(Type),
47    OneOf {
48        #[serde(rename = "oneOf")]
49        one_of: Vec<ReferenceOr<Schema>>,
50    },
51    AllOf {
52        #[serde(rename = "allOf")]
53        all_of: Vec<ReferenceOr<Schema>>,
54    },
55    AnyOf {
56        #[serde(rename = "anyOf")]
57        any_of: Vec<ReferenceOr<Schema>>,
58    },
59    Not {
60        not: Box<ReferenceOr<Schema>>,
61    },
62    Any(AnySchema),
63}
64
65// Custom Deserialize implementation that is similar to the logic for an
66// untagged enum with awareness of all the fields we might expect to see. This
67// is necessary to ensure that relevant fields aren't ignored e.g. when mixing
68// a object with a oneOf/anyOf/allOf. Note that serde's deny_unknown_fields
69// doesn't help us here due to its interactions with our abundant use of
70// flatten.
71impl<'de> Deserialize<'de> for SchemaKind {
72    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73    where
74        D: serde::Deserializer<'de>,
75    {
76        #[derive(Deserialize)]
77        #[serde(rename_all = "camelCase")]
78        struct RawAnySchema {
79            #[serde(rename = "type", default)]
80            typ: Option<String>,
81            #[serde(default)]
82            pattern: Option<String>,
83            #[serde(default)]
84            multiple_of: Option<serde_json::Number>,
85            #[serde(default)]
86            exclusive_minimum: Option<bool>,
87            #[serde(default)]
88            exclusive_maximum: Option<bool>,
89            #[serde(default)]
90            minimum: Option<serde_json::Number>,
91            #[serde(default)]
92            maximum: Option<serde_json::Number>,
93            #[serde(default)]
94            properties: Option<IndexMap<String, ReferenceOr<Box<Schema>>>>,
95            #[serde(default)]
96            required: Option<Vec<String>>,
97            #[serde(default)]
98            additional_properties: Option<AdditionalProperties>,
99            #[serde(default)]
100            min_properties: Option<usize>,
101            #[serde(default)]
102            max_properties: Option<usize>,
103            #[serde(default)]
104            items: Option<ReferenceOr<Box<Schema>>>,
105            #[serde(default)]
106            min_items: Option<usize>,
107            #[serde(default)]
108            max_items: Option<usize>,
109            #[serde(default)]
110            unique_items: Option<bool>,
111            #[serde(rename = "enum", default)]
112            enumeration: Option<Vec<serde_json::Value>>,
113            #[serde(default)]
114            format: Option<String>,
115            #[serde(default)]
116            min_length: Option<usize>,
117            #[serde(default)]
118            max_length: Option<usize>,
119            #[serde(default)]
120            one_of: Option<Vec<ReferenceOr<Schema>>>,
121            #[serde(default)]
122            all_of: Option<Vec<ReferenceOr<Schema>>>,
123            #[serde(default)]
124            any_of: Option<Vec<ReferenceOr<Schema>>>,
125            #[serde(default)]
126            not: Option<Box<ReferenceOr<Schema>>>,
127        }
128
129        let any = RawAnySchema::deserialize(deserializer)?;
130        match any {
131            // String
132            RawAnySchema {
133                typ: Some(typ),
134                pattern,
135                multiple_of: None,
136                exclusive_minimum: None,
137                exclusive_maximum: None,
138                minimum: None,
139                maximum: None,
140                properties: None,
141                required: None,
142                additional_properties: None,
143                min_properties: None,
144                max_properties: None,
145                items: None,
146                min_items: None,
147                max_items: None,
148                unique_items: None,
149                enumeration,
150                format,
151                min_length,
152                max_length,
153                one_of: None,
154                all_of: None,
155                any_of: None,
156                not: None,
157            } if typ == "string"
158                && enumerated_values_valid(&enumeration, serde_json::Value::is_string) =>
159            {
160                Ok(Self::Type(Type::String(StringType {
161                    format: format.into(),
162                    pattern,
163                    enumeration: enumerated_values_transform(enumeration, |v| {
164                        v.as_str().map(String::from)
165                    }),
166                    min_length,
167                    max_length,
168                })))
169            }
170
171            // Number
172            RawAnySchema {
173                typ: Some(typ),
174                pattern: None,
175                multiple_of,
176                exclusive_minimum,
177                exclusive_maximum,
178                minimum,
179                maximum,
180                properties: None,
181                required: None,
182                additional_properties: None,
183                min_properties: None,
184                max_properties: None,
185                items: None,
186                min_items: None,
187                max_items: None,
188                unique_items: None,
189                enumeration,
190                format,
191                min_length: None,
192                max_length: None,
193                one_of: None,
194                all_of: None,
195                any_of: None,
196                not: None,
197            } if typ == "number"
198                && enumerated_values_valid(&enumeration, serde_json::Value::is_number) =>
199            {
200                Ok(Self::Type(Type::Number(NumberType {
201                    format: format.into(),
202                    multiple_of: multiple_of.map(|v| v.as_f64().unwrap()),
203                    exclusive_minimum: exclusive_minimum.unwrap_or_default(),
204                    exclusive_maximum: exclusive_maximum.unwrap_or_default(),
205                    minimum: minimum.map(|v| v.as_f64().unwrap()),
206                    maximum: maximum.map(|v| v.as_f64().unwrap()),
207                    enumeration: enumerated_values_transform(
208                        enumeration,
209                        serde_json::Value::as_f64,
210                    ),
211                })))
212            }
213
214            // Integer
215            RawAnySchema {
216                typ: Some(typ),
217                pattern: None,
218                multiple_of,
219                exclusive_minimum,
220                exclusive_maximum,
221                minimum,
222                maximum,
223                properties: None,
224                required: None,
225                additional_properties: None,
226                min_properties: None,
227                max_properties: None,
228                items: None,
229                min_items: None,
230                max_items: None,
231                unique_items: None,
232                enumeration,
233                format,
234                min_length: None,
235                max_length: None,
236                one_of: None,
237                all_of: None,
238                any_of: None,
239                not: None,
240            } if typ == "integer"
241                && enumerated_values_valid(&enumeration, serde_json::Value::is_i64)
242                && none_or_int(&multiple_of)
243                && none_or_int(&minimum)
244                && none_or_int(&maximum) =>
245            {
246                Ok(Self::Type(Type::Integer(IntegerType {
247                    format: format.into(),
248                    multiple_of: multiple_of.map(|v| v.as_i64().unwrap()),
249                    exclusive_minimum: exclusive_minimum.unwrap_or_default(),
250                    exclusive_maximum: exclusive_maximum.unwrap_or_default(),
251                    minimum: minimum.map(|v| v.as_i64().unwrap()),
252                    maximum: maximum.map(|v| v.as_i64().unwrap()),
253                    enumeration: enumerated_values_transform(
254                        enumeration,
255                        serde_json::Value::as_i64,
256                    ),
257                })))
258            }
259
260            // Boolean
261            RawAnySchema {
262                typ: Some(typ),
263                pattern: None,
264                multiple_of: None,
265                exclusive_minimum: None,
266                exclusive_maximum: None,
267                minimum: None,
268                maximum: None,
269                properties: None,
270                required: None,
271                additional_properties: None,
272                min_properties: None,
273                max_properties: None,
274                items: None,
275                min_items: None,
276                max_items: None,
277                unique_items: None,
278                enumeration,
279                format: None,
280                min_length: None,
281                max_length: None,
282                one_of: None,
283                all_of: None,
284                any_of: None,
285                not: None,
286            } if typ == "boolean"
287                && enumerated_values_valid(&enumeration, serde_json::Value::is_boolean) =>
288            {
289                Ok(Self::Type(Type::Boolean(BooleanType {
290                    enumeration: enumerated_values_transform(
291                        enumeration,
292                        serde_json::Value::as_bool,
293                    ),
294                })))
295            }
296
297            // Object
298            RawAnySchema {
299                typ: Some(typ),
300                pattern: None,
301                multiple_of: None,
302                exclusive_minimum: None,
303                exclusive_maximum: None,
304                minimum: None,
305                maximum: None,
306                properties,
307                required,
308                additional_properties,
309                min_properties,
310                max_properties,
311                items: None,
312                min_items: None,
313                max_items: None,
314                unique_items: None,
315                enumeration: None,
316                format: None,
317                min_length: None,
318                max_length: None,
319                one_of: None,
320                all_of: None,
321                any_of: None,
322                not: None,
323            } if typ == "object" => Ok(Self::Type(Type::Object(ObjectType {
324                properties: properties.unwrap_or_default(),
325                required: required.unwrap_or_default(),
326                additional_properties,
327                min_properties,
328                max_properties,
329            }))),
330
331            // Array
332            RawAnySchema {
333                typ: Some(typ),
334                pattern: None,
335                multiple_of: None,
336                exclusive_minimum: None,
337                exclusive_maximum: None,
338                minimum: None,
339                maximum: None,
340                properties: None,
341                required: None,
342                additional_properties: None,
343                min_properties: None,
344                max_properties: None,
345                items,
346                min_items,
347                max_items,
348                unique_items,
349                enumeration: None,
350                format: None,
351                min_length: None,
352                max_length: None,
353                one_of: None,
354                all_of: None,
355                any_of: None,
356                not: None,
357            } if typ == "array" => Ok(Self::Type(Type::Array(ArrayType {
358                items,
359                min_items,
360                max_items,
361                unique_items: unique_items.unwrap_or_default(),
362            }))),
363
364            // OneOf
365            RawAnySchema {
366                typ: None,
367                pattern: None,
368                multiple_of: None,
369                exclusive_minimum: None,
370                exclusive_maximum: None,
371                minimum: None,
372                maximum: None,
373                properties: None,
374                required: None,
375                additional_properties: None,
376                min_properties: None,
377                max_properties: None,
378                items: None,
379                min_items: None,
380                max_items: None,
381                unique_items: None,
382                enumeration: None,
383                format: None,
384                min_length: None,
385                max_length: None,
386                one_of: Some(one_of),
387                all_of: None,
388                any_of: None,
389                not: None,
390            } => Ok(Self::OneOf { one_of }),
391
392            // AllOf
393            RawAnySchema {
394                typ: None,
395                pattern: None,
396                multiple_of: None,
397                exclusive_minimum: None,
398                exclusive_maximum: None,
399                minimum: None,
400                maximum: None,
401                properties: None,
402                required: None,
403                additional_properties: None,
404                min_properties: None,
405                max_properties: None,
406                items: None,
407                min_items: None,
408                max_items: None,
409                unique_items: None,
410                enumeration: None,
411                format: None,
412                min_length: None,
413                max_length: None,
414                one_of: None,
415                all_of: Some(all_of),
416                any_of: None,
417                not: None,
418            } => Ok(Self::AllOf { all_of }),
419
420            // AnyOf
421            RawAnySchema {
422                typ: None,
423                pattern: None,
424                multiple_of: None,
425                exclusive_minimum: None,
426                exclusive_maximum: None,
427                minimum: None,
428                maximum: None,
429                properties: None,
430                required: None,
431                additional_properties: None,
432                min_properties: None,
433                max_properties: None,
434                items: None,
435                min_items: None,
436                max_items: None,
437                unique_items: None,
438                enumeration: None,
439                format: None,
440                min_length: None,
441                max_length: None,
442                one_of: None,
443                all_of: None,
444                any_of: Some(any_of),
445                not: None,
446            } => Ok(Self::AnyOf { any_of }),
447
448            // Not
449            RawAnySchema {
450                typ: None,
451                pattern: None,
452                multiple_of: None,
453                exclusive_minimum: None,
454                exclusive_maximum: None,
455                minimum: None,
456                maximum: None,
457                properties: None,
458                required: None,
459                additional_properties: None,
460                min_properties: None,
461                max_properties: None,
462                items: None,
463                min_items: None,
464                max_items: None,
465                unique_items: None,
466                enumeration: None,
467                format: None,
468                min_length: None,
469                max_length: None,
470                one_of: None,
471                all_of: None,
472                any_of: None,
473                not: Some(not),
474            } => Ok(Self::Not { not }),
475
476            // Any
477            RawAnySchema {
478                typ,
479                pattern,
480                multiple_of,
481                exclusive_minimum,
482                exclusive_maximum,
483                minimum,
484                maximum,
485                properties,
486                required,
487                additional_properties,
488                min_properties,
489                max_properties,
490                items,
491                min_items,
492                max_items,
493                unique_items,
494                enumeration,
495                format,
496                min_length,
497                max_length,
498                one_of,
499                all_of,
500                any_of,
501                not,
502            } => Ok(Self::Any(AnySchema {
503                typ,
504                pattern,
505                multiple_of: multiple_of.map(|n| n.as_f64().unwrap()),
506                exclusive_minimum,
507                exclusive_maximum,
508                minimum: minimum.map(|n| n.as_f64().unwrap()),
509                maximum: maximum.map(|n| n.as_f64().unwrap()),
510                properties: properties.unwrap_or_default(),
511                required: required.unwrap_or_default(),
512                additional_properties,
513                min_properties,
514                max_properties,
515                items,
516                min_items,
517                max_items,
518                unique_items,
519                enumeration: enumeration.unwrap_or_default(),
520                format,
521                min_length,
522                max_length,
523                one_of: one_of.unwrap_or_default(),
524                all_of: all_of.unwrap_or_default(),
525                any_of: any_of.unwrap_or_default(),
526                not,
527            })),
528        }
529    }
530}
531
532fn none_or_int(value: &Option<serde_json::Number>) -> bool {
533    match value {
534        None => true,
535        Some(x) => x.is_i64(),
536    }
537}
538
539fn enumerated_values_transform<T, F>(
540    enumeration: Option<Vec<serde_json::Value>>,
541    transform: F,
542) -> Vec<Option<T>>
543where
544    F: Fn(&serde_json::Value) -> Option<T>,
545{
546    match enumeration {
547        Some(values) => values
548            .iter()
549            .map(|v| {
550                if v.is_null() {
551                    None
552                } else {
553                    Some(transform(v).unwrap())
554                }
555            })
556            .collect::<Vec<_>>(),
557        None => Default::default(),
558    }
559}
560
561fn enumerated_values_valid<F>(enumeration: &Option<Vec<serde_json::Value>>, check: F) -> bool
562where
563    F: Fn(&serde_json::Value) -> bool,
564{
565    match enumeration {
566        Some(values) => values.iter().all(|value| value.is_null() || check(value)),
567        None => true,
568    }
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
572#[serde(tag = "type", rename_all = "lowercase")]
573pub enum Type {
574    String(StringType),
575    Number(NumberType),
576    Integer(IntegerType),
577    Object(ObjectType),
578    Array(ArrayType),
579    Boolean(BooleanType),
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
583#[serde(untagged)]
584pub enum AdditionalProperties {
585    Any(bool),
586    Schema(Box<ReferenceOr<Schema>>),
587}
588
589/// Catch-all for any combination of properties that doesn't correspond to one
590/// of the predefined subsets.
591#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
592#[serde(rename_all = "camelCase")]
593pub struct AnySchema {
594    #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
595    pub typ: Option<String>,
596    #[serde(default, skip_serializing_if = "Option::is_none")]
597    pub pattern: Option<String>,
598    #[serde(default, skip_serializing_if = "Option::is_none")]
599    pub multiple_of: Option<f64>,
600    #[serde(default, skip_serializing_if = "Option::is_none")]
601    pub exclusive_minimum: Option<bool>,
602    #[serde(default, skip_serializing_if = "Option::is_none")]
603    pub exclusive_maximum: Option<bool>,
604    #[serde(default, skip_serializing_if = "Option::is_none")]
605    pub minimum: Option<f64>,
606    #[serde(default, skip_serializing_if = "Option::is_none")]
607    pub maximum: Option<f64>,
608    #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
609    pub properties: IndexMap<String, ReferenceOr<Box<Schema>>>,
610    #[serde(default, skip_serializing_if = "Vec::is_empty")]
611    pub required: Vec<String>,
612    #[serde(default, skip_serializing_if = "Option::is_none")]
613    pub additional_properties: Option<AdditionalProperties>,
614    #[serde(default, skip_serializing_if = "Option::is_none")]
615    pub min_properties: Option<usize>,
616    #[serde(default, skip_serializing_if = "Option::is_none")]
617    pub max_properties: Option<usize>,
618    #[serde(default, skip_serializing_if = "Option::is_none")]
619    pub items: Option<ReferenceOr<Box<Schema>>>,
620    #[serde(default, skip_serializing_if = "Option::is_none")]
621    pub min_items: Option<usize>,
622    #[serde(default, skip_serializing_if = "Option::is_none")]
623    pub max_items: Option<usize>,
624    #[serde(default, skip_serializing_if = "Option::is_none")]
625    pub unique_items: Option<bool>,
626    #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
627    pub enumeration: Vec<serde_json::Value>,
628    #[serde(default, skip_serializing_if = "Option::is_none")]
629    pub format: Option<String>,
630    #[serde(default, skip_serializing_if = "Option::is_none")]
631    pub min_length: Option<usize>,
632    #[serde(default, skip_serializing_if = "Option::is_none")]
633    pub max_length: Option<usize>,
634    #[serde(default, skip_serializing_if = "Vec::is_empty")]
635    pub one_of: Vec<ReferenceOr<Schema>>,
636    #[serde(default, skip_serializing_if = "Vec::is_empty")]
637    pub all_of: Vec<ReferenceOr<Schema>>,
638    #[serde(default, skip_serializing_if = "Vec::is_empty")]
639    pub any_of: Vec<ReferenceOr<Schema>>,
640    #[serde(default, skip_serializing_if = "Option::is_none")]
641    pub not: Option<Box<ReferenceOr<Schema>>>,
642}
643
644#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
645#[serde(rename_all = "camelCase")]
646pub struct StringType {
647    #[serde(default, skip_serializing_if = "VariantOrUnknownOrEmpty::is_empty")]
648    pub format: VariantOrUnknownOrEmpty<StringFormat>,
649    #[serde(skip_serializing_if = "Option::is_none")]
650    pub pattern: Option<String>,
651    #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
652    pub enumeration: Vec<Option<String>>,
653    #[serde(skip_serializing_if = "Option::is_none")]
654    pub min_length: Option<usize>,
655    #[serde(skip_serializing_if = "Option::is_none")]
656    pub max_length: Option<usize>,
657}
658
659#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
660#[serde(rename_all = "camelCase")]
661pub struct NumberType {
662    #[serde(default, skip_serializing_if = "VariantOrUnknownOrEmpty::is_empty")]
663    pub format: VariantOrUnknownOrEmpty<NumberFormat>,
664    #[serde(skip_serializing_if = "Option::is_none")]
665    pub multiple_of: Option<f64>,
666    #[serde(default, skip_serializing_if = "is_false")]
667    pub exclusive_minimum: bool,
668    #[serde(default, skip_serializing_if = "is_false")]
669    pub exclusive_maximum: bool,
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub minimum: Option<f64>,
672    #[serde(skip_serializing_if = "Option::is_none")]
673    pub maximum: Option<f64>,
674    #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
675    pub enumeration: Vec<Option<f64>>,
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
679#[serde(rename_all = "camelCase")]
680pub struct IntegerType {
681    #[serde(default, skip_serializing_if = "VariantOrUnknownOrEmpty::is_empty")]
682    pub format: VariantOrUnknownOrEmpty<IntegerFormat>,
683    #[serde(skip_serializing_if = "Option::is_none")]
684    pub multiple_of: Option<i64>,
685    #[serde(default, skip_serializing_if = "is_false")]
686    pub exclusive_minimum: bool,
687    #[serde(default, skip_serializing_if = "is_false")]
688    pub exclusive_maximum: bool,
689    #[serde(skip_serializing_if = "Option::is_none")]
690    pub minimum: Option<i64>,
691    #[serde(skip_serializing_if = "Option::is_none")]
692    pub maximum: Option<i64>,
693    #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
694    pub enumeration: Vec<Option<i64>>,
695}
696
697#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
698#[serde(rename_all = "camelCase")]
699pub struct ObjectType {
700    #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
701    pub properties: IndexMap<String, ReferenceOr<Box<Schema>>>,
702    #[serde(default, skip_serializing_if = "Vec::is_empty")]
703    pub required: Vec<String>,
704    #[serde(skip_serializing_if = "Option::is_none")]
705    pub additional_properties: Option<AdditionalProperties>,
706    #[serde(skip_serializing_if = "Option::is_none")]
707    pub min_properties: Option<usize>,
708    #[serde(skip_serializing_if = "Option::is_none")]
709    pub max_properties: Option<usize>,
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
713#[serde(rename_all = "camelCase")]
714pub struct ArrayType {
715    #[serde(skip_serializing_if = "Option::is_none")]
716    pub items: Option<ReferenceOr<Box<Schema>>>,
717    #[serde(skip_serializing_if = "Option::is_none")]
718    pub min_items: Option<usize>,
719    #[serde(skip_serializing_if = "Option::is_none")]
720    pub max_items: Option<usize>,
721    #[serde(default, skip_serializing_if = "is_false")]
722    pub unique_items: bool,
723}
724
725#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
726#[serde(rename_all = "camelCase")]
727pub struct BooleanType {
728    #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
729    pub enumeration: Vec<Option<bool>>,
730}
731
732#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
733#[serde(rename_all = "lowercase")]
734pub enum NumberFormat {
735    Float,
736    Double,
737}
738
739impl FromStr for NumberFormat {
740    type Err = ();
741
742    fn from_str(s: &str) -> Result<Self, Self::Err> {
743        match s {
744            "float" => Ok(Self::Float),
745            "double" => Ok(Self::Double),
746            _ => Err(()),
747        }
748    }
749}
750
751#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
752#[serde(rename_all = "lowercase")]
753pub enum IntegerFormat {
754    Int32,
755    Int64,
756}
757
758impl FromStr for IntegerFormat {
759    type Err = ();
760
761    fn from_str(s: &str) -> Result<Self, Self::Err> {
762        match s {
763            "int32" => Ok(Self::Int32),
764            "int64" => Ok(Self::Int64),
765            _ => Err(()),
766        }
767    }
768}
769
770#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
771#[serde(rename_all = "kebab-case")]
772pub enum StringFormat {
773    Date,
774    DateTime,
775    Password,
776    Byte,
777    Binary,
778}
779
780impl FromStr for StringFormat {
781    type Err = ();
782
783    fn from_str(s: &str) -> Result<Self, Self::Err> {
784        match s {
785            "date" => Ok(Self::Date),
786            "date-time" => Ok(Self::DateTime),
787            "password" => Ok(Self::Password),
788            "byte" => Ok(Self::Byte),
789            "binary" => Ok(Self::Binary),
790            _ => Err(()),
791        }
792    }
793}
794
795#[cfg(test)]
796mod tests {
797    use serde_json::json;
798
799    use crate::{
800        AnySchema, Schema, SchemaData, SchemaKind, StringType, Type, VariantOrUnknownOrEmpty,
801    };
802
803    #[test]
804    fn test_schema_with_extensions() {
805        let schema = serde_json::from_str::<Schema>(
806            r#"{
807                "type": "boolean",
808                "x-foo": "bar"
809            }"#,
810        )
811        .unwrap();
812
813        assert_eq!(
814            schema.schema_data.extensions.get("x-foo"),
815            Some(&json!("bar"))
816        );
817    }
818
819    #[test]
820    fn test_any() {
821        let value = json! { {} };
822        serde_json::from_value::<AnySchema>(value).unwrap();
823    }
824
825    #[test]
826    fn test_not() {
827        let value = json! {
828            {
829                "not": {}
830            }
831        };
832
833        let schema = serde_json::from_value::<Schema>(value).unwrap();
834        assert!(matches!(schema.schema_kind, SchemaKind::Not { not: _ }));
835    }
836
837    #[test]
838    fn test_null() {
839        let value = json! {
840            {
841                "nullable": true,
842                "enum": [ null ],
843            }
844        };
845
846        let schema = serde_json::from_value::<Schema>(value).unwrap();
847        assert!(matches!(
848            &schema.schema_data,
849            SchemaData { nullable: true, .. }
850        ));
851        assert!(matches!(
852            &schema.schema_kind,
853            SchemaKind::Any(AnySchema { enumeration, .. }) if enumeration[0] == json!(null)));
854    }
855
856    #[test]
857    fn test_object_and_one_of() {
858        let value = json! {
859            {
860                "type": "object",
861                "nullable": true,
862                "description": "xyz",
863                "properties": {
864                    "a": {},
865                    "b": {},
866                    "c": {}
867                },
868                "oneOf": [
869                    { "required": ["a"] },
870                    { "required": ["b"] },
871                    { "required": ["c"] }
872                ],
873                "x-foo": "bar"
874            }
875        };
876
877        let schema = serde_json::from_value::<Schema>(value).unwrap();
878        assert!(schema.schema_data.nullable);
879        assert_eq!(schema.schema_data.extensions.get("x-foo").unwrap(), "bar");
880
881        match schema.schema_kind {
882            SchemaKind::Any(AnySchema {
883                typ,
884                properties,
885                one_of,
886                ..
887            }) => {
888                assert_eq!(typ.unwrap(), "object");
889                assert_eq!(properties.len(), 3);
890                assert_eq!(one_of.len(), 3);
891            }
892            _ => panic!("incorrect kind {:#?}", schema),
893        }
894    }
895
896    #[test]
897    fn test_enum_with_null() {
898        let value = json! {
899            {
900                "type": "string",
901                "nullable": true,
902                "enum": [ null, "howdy" ]
903            }
904        };
905
906        let schema = serde_json::from_value::<Schema>(value).unwrap();
907        assert!(schema.schema_data.nullable);
908
909        match schema.schema_kind {
910            SchemaKind::Type(Type::String(StringType {
911                format: VariantOrUnknownOrEmpty::Empty,
912                pattern: None,
913                enumeration,
914                min_length: None,
915                max_length: None,
916            })) => {
917                assert_eq!(enumeration, vec![None, Some("howdy".to_string())]);
918            }
919            _ => panic!("incorrect kind {:#?}", schema),
920        }
921    }
922}