utoipa/openapi/
schema.rs

1//! Implements [OpenAPI Schema Object][schema] types which can be
2//! used to define field properties, enum values, array or object types.
3//!
4//! [schema]: https://spec.openapis.org/oas/latest.html#schema-object
5use std::collections::BTreeMap;
6
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use super::RefOr;
11use super::{builder, security::SecurityScheme, set_value, xml::Xml, Deprecated, Response};
12use crate::{ToResponse, ToSchema};
13
14macro_rules! component_from_builder {
15    ( $name:ident ) => {
16        impl From<$name> for Schema {
17            fn from(builder: $name) -> Self {
18                builder.build().into()
19            }
20        }
21    };
22}
23
24macro_rules! to_array_builder {
25    () => {
26        /// Construct a new [`ArrayBuilder`] with this component set to [`ArrayBuilder::items`].
27        pub fn to_array_builder(self) -> ArrayBuilder {
28            ArrayBuilder::from(Array::new(self))
29        }
30    };
31}
32
33/// Create an _`empty`_ [`Schema`] that serializes to _`null`_.
34///
35/// Can be used in places where an item can be serialized as `null`. This is used with unit type
36/// enum variants and tuple unit types.
37pub fn empty() -> Schema {
38    Schema::Object(
39        ObjectBuilder::new()
40            .schema_type(SchemaType::Value)
41            .nullable(true)
42            .default(Some(serde_json::Value::Null))
43            .into(),
44    )
45}
46
47builder! {
48    ComponentsBuilder;
49
50    /// Implements [OpenAPI Components Object][components] which holds supported
51    /// reusable objects.
52    ///
53    /// Components can hold either reusable types themselves or references to other reusable
54    /// types.
55    ///
56    /// [components]: https://spec.openapis.org/oas/latest.html#components-object
57    #[non_exhaustive]
58    #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
59    #[cfg_attr(feature = "debug", derive(Debug))]
60    #[serde(rename_all = "camelCase")]
61    pub struct Components {
62        /// Map of reusable [OpenAPI Schema Object][schema]s.
63        ///
64        /// [schema]: https://spec.openapis.org/oas/latest.html#schema-object
65        #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
66        pub schemas: BTreeMap<String, RefOr<Schema>>,
67
68        /// Map of reusable response name, to [OpenAPI Response Object][response]s or [OpenAPI
69        /// Reference][reference]s to [OpenAPI Response Object][response]s.
70        ///
71        /// [response]: https://spec.openapis.org/oas/latest.html#response-object
72        /// [reference]: https://spec.openapis.org/oas/latest.html#reference-object
73        #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
74        pub responses: BTreeMap<String, RefOr<Response>>,
75
76        /// Map of reusable [OpenAPI Security Scheme Object][security_scheme]s.
77        ///
78        /// [security_scheme]: https://spec.openapis.org/oas/latest.html#security-scheme-object
79        #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
80        pub security_schemes: BTreeMap<String, SecurityScheme>,
81    }
82}
83
84impl Components {
85    /// Construct a new [`Components`].
86    pub fn new() -> Self {
87        Self {
88            ..Default::default()
89        }
90    }
91    /// Add [`SecurityScheme`] to [`Components`]
92    ///
93    /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
94    /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
95    ///
96    /// [requirement]: ../security/struct.SecurityRequirement.html
97    pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
98        &mut self,
99        name: N,
100        security_schema: S,
101    ) {
102        self.security_schemes
103            .insert(name.into(), security_schema.into());
104    }
105
106    /// Add iterator of [`SecurityScheme`]s to [`Components`].
107    ///
108    /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
109    /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
110    ///
111    /// [requirement]: ../security/struct.SecurityRequirement.html
112    pub fn add_security_schemes_from_iter<
113        I: IntoIterator<Item = (N, S)>,
114        N: Into<String>,
115        S: Into<SecurityScheme>,
116    >(
117        &mut self,
118        schemas: I,
119    ) {
120        self.security_schemes.extend(
121            schemas
122                .into_iter()
123                .map(|(name, item)| (name.into(), item.into())),
124        );
125    }
126}
127
128impl ComponentsBuilder {
129    /// Add [`Schema`] to [`Components`].
130    ///
131    /// Accepts two arguments where first is name of the schema and second is the schema itself.
132    pub fn schema<S: Into<String>, I: Into<RefOr<Schema>>>(mut self, name: S, schema: I) -> Self {
133        self.schemas.insert(name.into(), schema.into());
134
135        self
136    }
137
138    pub fn schema_from<'s, I: ToSchema<'s>>(mut self) -> Self {
139        let aliases = I::aliases();
140
141        // TODO a temporal hack to add the main schema only if there are no aliases pre-defined.
142        // Eventually aliases functionality should be extracted out from the `ToSchema`. Aliases
143        // are created when the main schema is a generic type which should be included in OpenAPI
144        // spec in its generic form.
145        if aliases.is_empty() {
146            let (name, schema) = I::schema();
147            self.schemas.insert(name.to_string(), schema);
148        }
149
150        self.schemas_from_iter(aliases)
151    }
152
153    /// Add [`Schema`]s from iterator.
154    ///
155    /// # Examples
156    /// ```rust
157    /// # use utoipa::openapi::schema::{ComponentsBuilder, ObjectBuilder,
158    /// #    SchemaType, Schema};
159    /// ComponentsBuilder::new().schemas_from_iter([(
160    ///     "Pet",
161    ///     Schema::from(
162    ///         ObjectBuilder::new()
163    ///             .property(
164    ///                 "name",
165    ///                 ObjectBuilder::new().schema_type(SchemaType::String),
166    ///             )
167    ///             .required("name")
168    ///     ),
169    /// )]);
170    /// ```
171    pub fn schemas_from_iter<
172        I: IntoIterator<Item = (S, C)>,
173        C: Into<RefOr<Schema>>,
174        S: Into<String>,
175    >(
176        mut self,
177        schemas: I,
178    ) -> Self {
179        self.schemas.extend(
180            schemas
181                .into_iter()
182                .map(|(name, schema)| (name.into(), schema.into())),
183        );
184
185        self
186    }
187
188    pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
189        mut self,
190        name: S,
191        response: R,
192    ) -> Self {
193        self.responses.insert(name.into(), response.into());
194        self
195    }
196
197    pub fn response_from<'r, I: ToResponse<'r>>(self) -> Self {
198        let (name, response) = I::response();
199        self.response(name, response)
200    }
201
202    pub fn responses_from_iter<
203        I: IntoIterator<Item = (S, R)>,
204        S: Into<String>,
205        R: Into<RefOr<Response>>,
206    >(
207        mut self,
208        responses: I,
209    ) -> Self {
210        self.responses.extend(
211            responses
212                .into_iter()
213                .map(|(name, response)| (name.into(), response.into())),
214        );
215
216        self
217    }
218
219    /// Add [`SecurityScheme`] to [`Components`].
220    ///
221    /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
222    /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
223    ///
224    /// [requirement]: ../security/struct.SecurityRequirement.html
225    pub fn security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
226        mut self,
227        name: N,
228        security_schema: S,
229    ) -> Self {
230        self.security_schemes
231            .insert(name.into(), security_schema.into());
232
233        self
234    }
235}
236
237/// Is super type for [OpenAPI Schema Object][schemas]. Schema is reusable resource what can be
238/// referenced from path operations and other components using [`Ref`].
239///
240/// [schemas]: https://spec.openapis.org/oas/latest.html#schema-object
241#[non_exhaustive]
242#[derive(Serialize, Deserialize, Clone, PartialEq)]
243#[cfg_attr(feature = "debug", derive(Debug))]
244#[serde(untagged, rename_all = "camelCase")]
245pub enum Schema {
246    /// Defines array schema from another schema. Typically used with
247    /// [`Schema::Object`]. Slice and Vec types are translated to [`Schema::Array`] types.
248    Array(Array),
249    /// Defines object schema. Object is either `object` holding **properties** which are other [`Schema`]s
250    /// or can be a field within the [`Object`].
251    Object(Object),
252    /// Creates a _OneOf_ type [composite Object][composite] schema. This schema
253    /// is used to map multiple schemas together where API endpoint could return any of them.
254    /// [`Schema::OneOf`] is created form complex enum where enum holds other than unit types.
255    ///
256    /// [composite]: https://spec.openapis.org/oas/latest.html#components-object
257    OneOf(OneOf),
258
259    /// Creates a _AllOf_ type [composite Object][composite] schema.
260    ///
261    /// [composite]: https://spec.openapis.org/oas/latest.html#components-object
262    AllOf(AllOf),
263
264    /// Creates a _AnyOf_ type [composite Object][composite] schema.
265    ///
266    /// [composite]: https://spec.openapis.org/oas/latest.html#components-object
267    AnyOf(AnyOf),
268}
269
270impl Default for Schema {
271    fn default() -> Self {
272        Schema::Object(Object::default())
273    }
274}
275
276/// OpenAPI [Discriminator][discriminator] object which can be optionally used together with
277/// [`OneOf`] composite object.
278///
279/// [discriminator]: https://spec.openapis.org/oas/latest.html#discriminator-object
280#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
281#[serde(rename_all = "camelCase")]
282#[cfg_attr(feature = "debug", derive(Debug))]
283pub struct Discriminator {
284    /// Defines a discriminator property name which must be found within all composite
285    /// objects.
286    pub property_name: String,
287}
288
289impl Discriminator {
290    /// Construct a new [`Discriminator`] object with property name.
291    ///
292    /// # Examples
293    ///
294    /// Create a new [`Discriminator`] object for `pet_type` property.
295    /// ```rust
296    /// # use utoipa::openapi::schema::Discriminator;
297    /// let discriminator = Discriminator::new("pet_type");
298    /// ```
299    pub fn new<I: Into<String>>(property_name: I) -> Self {
300        Self {
301            property_name: property_name.into(),
302        }
303    }
304}
305
306builder! {
307    OneOfBuilder;
308
309    /// OneOf [Composite Object][oneof] component holds
310    /// multiple components together where API endpoint could return any of them.
311    ///
312    /// See [`Schema::OneOf`] for more details.
313    ///
314    /// [oneof]: https://spec.openapis.org/oas/latest.html#components-object
315    #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
316    #[cfg_attr(feature = "debug", derive(Debug))]
317    pub struct OneOf {
318        /// Components of _OneOf_ component.
319        #[serde(rename = "oneOf")]
320        pub items: Vec<RefOr<Schema>>,
321
322        /// Changes the [`OneOf`] title.
323        #[serde(skip_serializing_if = "Option::is_none")]
324        pub title: Option<String>,
325
326        /// Description of the [`OneOf`]. Markdown syntax is supported.
327        #[serde(skip_serializing_if = "Option::is_none")]
328        pub description: Option<String>,
329
330        /// Default value which is provided when user has not provided the input in Swagger UI.
331        #[serde(skip_serializing_if = "Option::is_none")]
332        pub default: Option<Value>,
333
334        /// Example shown in UI of the value for richer documentation.
335        #[serde(skip_serializing_if = "Option::is_none")]
336        pub example: Option<Value>,
337
338        /// Optional discriminator field can be used to aid deserialization, serialization and validation of a
339        /// specific schema.
340        #[serde(skip_serializing_if = "Option::is_none")]
341        pub discriminator: Option<Discriminator>,
342
343        /// Set `true` to allow `"null"` to be used as value for given type.
344        #[serde(default, skip_serializing_if = "is_false")]
345        pub nullable: bool,
346    }
347}
348
349impl OneOf {
350    /// Construct a new [`OneOf`] component.
351    pub fn new() -> Self {
352        Self {
353            ..Default::default()
354        }
355    }
356
357    /// Construct a new [`OneOf`] component with given capacity.
358    ///
359    /// OneOf component is then able to contain number of components without
360    /// reallocating.
361    ///
362    /// # Examples
363    ///
364    /// Create [`OneOf`] component with initial capacity of 5.
365    /// ```rust
366    /// # use utoipa::openapi::schema::OneOf;
367    /// let one_of = OneOf::with_capacity(5);
368    /// ```
369    pub fn with_capacity(capacity: usize) -> Self {
370        Self {
371            items: Vec::with_capacity(capacity),
372            ..Default::default()
373        }
374    }
375}
376
377impl OneOfBuilder {
378    /// Adds a given [`Schema`] to [`OneOf`] [Composite Object][composite]
379    ///
380    /// [composite]: https://spec.openapis.org/oas/latest.html#components-object
381    pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
382        self.items.push(component.into());
383
384        self
385    }
386
387    /// Add or change the title of the [`OneOf`].
388    pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
389        set_value!(self title title.map(|title| title.into()))
390    }
391
392    /// Add or change optional description for `OneOf` component.
393    pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
394        set_value!(self description description.map(|description| description.into()))
395    }
396
397    /// Add or change default value for the object which is provided when user has not provided the input in Swagger UI.
398    pub fn default(mut self, default: Option<Value>) -> Self {
399        set_value!(self default default)
400    }
401
402    /// Add or change example shown in UI of the value for richer documentation.
403    pub fn example(mut self, example: Option<Value>) -> Self {
404        set_value!(self example example)
405    }
406
407    /// Add or change discriminator field of the composite [`OneOf`] type.
408    pub fn discriminator(mut self, discriminator: Option<Discriminator>) -> Self {
409        set_value!(self discriminator discriminator)
410    }
411
412    /// Add or change nullable flag for [`Object`].
413    pub fn nullable(mut self, nullable: bool) -> Self {
414        set_value!(self nullable nullable)
415    }
416
417    to_array_builder!();
418}
419
420impl From<OneOf> for Schema {
421    fn from(one_of: OneOf) -> Self {
422        Self::OneOf(one_of)
423    }
424}
425
426impl From<OneOfBuilder> for RefOr<Schema> {
427    fn from(one_of: OneOfBuilder) -> Self {
428        Self::T(Schema::OneOf(one_of.build()))
429    }
430}
431
432component_from_builder!(OneOfBuilder);
433
434builder! {
435    AllOfBuilder;
436
437    /// AllOf [Composite Object][allof] component holds
438    /// multiple components together where API endpoint will return a combination of all of them.
439    ///
440    /// See [`Schema::AllOf`] for more details.
441    ///
442    /// [allof]: https://spec.openapis.org/oas/latest.html#components-object
443    #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
444    #[cfg_attr(feature = "debug", derive(Debug))]
445    pub struct AllOf {
446        /// Components of _AllOf_ component.
447        #[serde(rename = "allOf")]
448        pub items: Vec<RefOr<Schema>>,
449
450        /// Changes the [`AllOf`] title.
451        #[serde(skip_serializing_if = "Option::is_none")]
452        pub title: Option<String>,
453
454        /// Description of the [`AllOf`]. Markdown syntax is supported.
455        #[serde(skip_serializing_if = "Option::is_none")]
456        pub description: Option<String>,
457
458        /// Default value which is provided when user has not provided the input in Swagger UI.
459        #[serde(skip_serializing_if = "Option::is_none")]
460        pub default: Option<Value>,
461
462        /// Example shown in UI of the value for richer documentation.
463        #[serde(skip_serializing_if = "Option::is_none")]
464        pub example: Option<Value>,
465
466        /// Optional discriminator field can be used to aid deserialization, serialization and validation of a
467        /// specific schema.
468        #[serde(skip_serializing_if = "Option::is_none")]
469        pub discriminator: Option<Discriminator>,
470
471        /// Set `true` to allow `"null"` to be used as value for given type.
472        #[serde(default, skip_serializing_if = "is_false")]
473        pub nullable: bool,
474    }
475}
476
477impl AllOf {
478    /// Construct a new [`AllOf`] component.
479    pub fn new() -> Self {
480        Self {
481            ..Default::default()
482        }
483    }
484
485    /// Construct a new [`AllOf`] component with given capacity.
486    ///
487    /// AllOf component is then able to contain number of components without
488    /// reallocating.
489    ///
490    /// # Examples
491    ///
492    /// Create [`AllOf`] component with initial capacity of 5.
493    /// ```rust
494    /// # use utoipa::openapi::schema::AllOf;
495    /// let one_of = AllOf::with_capacity(5);
496    /// ```
497    pub fn with_capacity(capacity: usize) -> Self {
498        Self {
499            items: Vec::with_capacity(capacity),
500            ..Default::default()
501        }
502    }
503}
504
505impl AllOfBuilder {
506    /// Adds a given [`Schema`] to [`AllOf`] [Composite Object][composite]
507    ///
508    /// [composite]: https://spec.openapis.org/oas/latest.html#components-object
509    pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
510        self.items.push(component.into());
511
512        self
513    }
514
515    /// Add or change the title of the [`AllOf`].
516    pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
517        set_value!(self title title.map(|title| title.into()))
518    }
519
520    /// Add or change optional description for `AllOf` component.
521    pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
522        set_value!(self description description.map(|description| description.into()))
523    }
524
525    /// Add or change default value for the object which is provided when user has not provided the input in Swagger UI.
526    pub fn default(mut self, default: Option<Value>) -> Self {
527        set_value!(self default default)
528    }
529
530    /// Add or change example shown in UI of the value for richer documentation.
531    pub fn example(mut self, example: Option<Value>) -> Self {
532        set_value!(self example example)
533    }
534
535    /// Add or change discriminator field of the composite [`AllOf`] type.
536    pub fn discriminator(mut self, discriminator: Option<Discriminator>) -> Self {
537        set_value!(self discriminator discriminator)
538    }
539
540    /// Add or change nullable flag for [`Object`].
541    pub fn nullable(mut self, nullable: bool) -> Self {
542        set_value!(self nullable nullable)
543    }
544
545    to_array_builder!();
546}
547
548impl From<AllOf> for Schema {
549    fn from(one_of: AllOf) -> Self {
550        Self::AllOf(one_of)
551    }
552}
553
554impl From<AllOfBuilder> for RefOr<Schema> {
555    fn from(one_of: AllOfBuilder) -> Self {
556        Self::T(Schema::AllOf(one_of.build()))
557    }
558}
559
560component_from_builder!(AllOfBuilder);
561
562builder! {
563    AnyOfBuilder;
564
565    /// AnyOf [Composite Object][anyof] component holds
566    /// multiple components together where API endpoint will return a combination of one or more of them.
567    ///
568    /// See [`Schema::AnyOf`] for more details.
569    ///
570    /// [anyof]: https://spec.openapis.org/oas/latest.html#components-object
571    #[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
572    #[cfg_attr(feature = "debug", derive(Debug))]
573    pub struct AnyOf {
574        /// Components of _AnyOf component.
575        #[serde(rename = "anyOf")]
576        pub items: Vec<RefOr<Schema>>,
577
578        /// Description of the [`AnyOf`]. Markdown syntax is supported.
579        #[serde(skip_serializing_if = "Option::is_none")]
580        pub description: Option<String>,
581
582        /// Default value which is provided when user has not provided the input in Swagger UI.
583        #[serde(skip_serializing_if = "Option::is_none")]
584        pub default: Option<Value>,
585
586        /// Example shown in UI of the value for richer documentation.
587        #[serde(skip_serializing_if = "Option::is_none")]
588        pub example: Option<Value>,
589
590        /// Optional discriminator field can be used to aid deserialization, serialization and validation of a
591        /// specific schema.
592        #[serde(skip_serializing_if = "Option::is_none")]
593        pub discriminator: Option<Discriminator>,
594
595        /// Set `true` to allow `"null"` to be used as value for given type.
596        #[serde(default, skip_serializing_if = "is_false")]
597        pub nullable: bool,
598    }
599}
600
601impl AnyOf {
602    /// Construct a new [`AnyOf`] component.
603    pub fn new() -> Self {
604        Self {
605            ..Default::default()
606        }
607    }
608
609    /// Construct a new [`AnyOf`] component with given capacity.
610    ///
611    /// AnyOf component is then able to contain number of components without
612    /// reallocating.
613    ///
614    /// # Examples
615    ///
616    /// Create [`AnyOf`] component with initial capacity of 5.
617    /// ```rust
618    /// # use utoipa::openapi::schema::AnyOf;
619    /// let one_of = AnyOf::with_capacity(5);
620    /// ```
621    pub fn with_capacity(capacity: usize) -> Self {
622        Self {
623            items: Vec::with_capacity(capacity),
624            ..Default::default()
625        }
626    }
627}
628
629impl AnyOfBuilder {
630    /// Adds a given [`Schema`] to [`AnyOf`] [Composite Object][composite]
631    ///
632    /// [composite]: https://spec.openapis.org/oas/latest.html#components-object
633    pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
634        self.items.push(component.into());
635
636        self
637    }
638
639    /// Add or change optional description for `AnyOf` component.
640    pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
641        set_value!(self description description.map(|description| description.into()))
642    }
643
644    /// Add or change default value for the object which is provided when user has not provided the input in Swagger UI.
645    pub fn default(mut self, default: Option<Value>) -> Self {
646        set_value!(self default default)
647    }
648
649    /// Add or change example shown in UI of the value for richer documentation.
650    pub fn example(mut self, example: Option<Value>) -> Self {
651        set_value!(self example example)
652    }
653
654    /// Add or change discriminator field of the composite [`AnyOf`] type.
655    pub fn discriminator(mut self, discriminator: Option<Discriminator>) -> Self {
656        set_value!(self discriminator discriminator)
657    }
658
659    /// Add or change nullable flag for [`AnyOf`].
660    pub fn nullable(mut self, nullable: bool) -> Self {
661        set_value!(self nullable nullable)
662    }
663
664    to_array_builder!();
665}
666
667impl From<AnyOf> for Schema {
668    fn from(any_of: AnyOf) -> Self {
669        Self::AnyOf(any_of)
670    }
671}
672
673impl From<AnyOfBuilder> for RefOr<Schema> {
674    fn from(any_of: AnyOfBuilder) -> Self {
675        Self::T(Schema::AnyOf(any_of.build()))
676    }
677}
678
679component_from_builder!(AnyOfBuilder);
680
681#[cfg(not(feature = "preserve_order"))]
682type ObjectPropertiesMap<K, V> = BTreeMap<K, V>;
683#[cfg(feature = "preserve_order")]
684type ObjectPropertiesMap<K, V> = indexmap::IndexMap<K, V>;
685
686builder! {
687    ObjectBuilder;
688
689    /// Implements subset of [OpenAPI Schema Object][schema] which allows
690    /// adding other [`Schema`]s as **properties** to this [`Schema`].
691    ///
692    /// This is a generic OpenAPI schema object which can used to present `object`, `field` or an `enum`.
693    ///
694    /// [schema]: https://spec.openapis.org/oas/latest.html#schema-object
695    #[non_exhaustive]
696    #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
697    #[cfg_attr(feature = "debug", derive(Debug))]
698    #[serde(rename_all = "camelCase")]
699    pub struct Object {
700        /// Type of [`Object`] e.g. [`SchemaType::Object`] for `object` and [`SchemaType::String`] for
701        /// `string` types.
702        #[serde(rename = "type", skip_serializing_if="SchemaType::is_value")]
703        pub schema_type: SchemaType,
704
705        /// Changes the [`Object`] title.
706        #[serde(skip_serializing_if = "Option::is_none")]
707        pub title: Option<String>,
708
709        /// Additional format for detailing the schema type.
710        #[serde(skip_serializing_if = "Option::is_none")]
711        pub format: Option<SchemaFormat>,
712
713        /// Description of the [`Object`]. Markdown syntax is supported.
714        #[serde(skip_serializing_if = "Option::is_none")]
715        pub description: Option<String>,
716
717        /// Default value which is provided when user has not provided the input in Swagger UI.
718        #[serde(skip_serializing_if = "Option::is_none")]
719        pub default: Option<Value>,
720
721        /// Enum variants of fields that can be represented as `unit` type `enums`
722        #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
723        pub enum_values: Option<Vec<Value>>,
724
725        /// Vector of required field names.
726        #[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
727        pub required: Vec<String>,
728
729        /// Map of fields with their [`Schema`] types.
730        ///
731        /// With **preserve_order** feature flag [`indexmap::IndexMap`] will be used as
732        /// properties map backing implementation to retain property order of [`ToSchema`][to_schema].
733        /// By default [`BTreeMap`] will be used.
734        ///
735        /// [to_schema]: crate::ToSchema
736        #[serde(skip_serializing_if = "ObjectPropertiesMap::is_empty", default = "ObjectPropertiesMap::new")]
737        pub properties: ObjectPropertiesMap<String, RefOr<Schema>>,
738
739        /// Additional [`Schema`] for non specified fields (Useful for typed maps).
740        #[serde(skip_serializing_if = "Option::is_none")]
741        pub additional_properties: Option<Box<AdditionalProperties<Schema>>>,
742
743        /// Changes the [`Object`] deprecated status.
744        #[serde(skip_serializing_if = "Option::is_none")]
745        pub deprecated: Option<Deprecated>,
746
747        /// Example shown in UI of the value for richer documentation.
748        #[serde(skip_serializing_if = "Option::is_none")]
749        pub example: Option<Value>,
750
751        /// Write only property will be only sent in _write_ requests like _POST, PUT_.
752        #[serde(skip_serializing_if = "Option::is_none")]
753        pub write_only: Option<bool>,
754
755        /// Read only property will be only sent in _read_ requests like _GET_.
756        #[serde(skip_serializing_if = "Option::is_none")]
757        pub read_only: Option<bool>,
758
759        /// Additional [`Xml`] formatting of the [`Object`].
760        #[serde(skip_serializing_if = "Option::is_none")]
761        pub xml: Option<Xml>,
762
763        /// Set `true` to allow `"null"` to be used as value for given type.
764        #[serde(default, skip_serializing_if = "is_false")]
765        pub nullable: bool,
766
767        /// Must be a number strictly greater than `0`. Numeric value is considered valid if value
768        /// divided by the _`multiple_of`_ value results an integer.
769        #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
770        pub multiple_of: Option<f64>,
771
772        /// Specify inclusive upper limit for the [`Object`]'s value. Number is considered valid if
773        /// it is equal or less than the _`maximum`_.
774        #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
775        pub maximum: Option<f64>,
776
777        /// Specify inclusive lower limit for the [`Object`]'s value. Number value is considered
778        /// valid if it is equal or greater than the _`minimum`_.
779        #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
780        pub minimum: Option<f64>,
781
782        /// Specify exclusive upper limit for the [`Object`]'s value. Number value is considered
783        /// valid if it is strictly less than _`exclusive_maximum`_.
784        #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
785        pub exclusive_maximum: Option<f64>,
786
787        /// Specify exclusive lower limit for the [`Object`]'s value. Number value is considered
788        /// valid if it is strictly above the _`exclusive_minimum`_.
789        #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
790        pub exclusive_minimum: Option<f64>,
791
792        /// Specify maximum length for `string` values. _`max_length`_ cannot be a negative integer
793        /// value. Value is considered valid if content length is equal or less than the _`max_length`_.
794        #[serde(skip_serializing_if = "Option::is_none")]
795        pub max_length: Option<usize>,
796
797        /// Specify minimum length for `string` values. _`min_length`_ cannot be a negative integer
798        /// value. Setting this to _`0`_ has the same effect as omitting this field. Value is
799        /// considered valid if content length is equal or more than the _`min_length`_.
800        #[serde(skip_serializing_if = "Option::is_none")]
801        pub min_length: Option<usize>,
802
803        /// Define a valid `ECMA-262` dialect regular expression. The `string` content is
804        /// considered valid if the _`pattern`_ matches the value successfully.
805        #[serde(skip_serializing_if = "Option::is_none")]
806        pub pattern: Option<String>,
807
808        /// Specify inclusive maximum amount of properties an [`Object`] can hold.
809        #[serde(skip_serializing_if = "Option::is_none")]
810        pub max_properties: Option<usize>,
811
812        /// Specify inclusive minimum amount of properties an [`Object`] can hold. Setting this to
813        /// `0` will have same effect as omitting the attribute.
814        #[serde(skip_serializing_if = "Option::is_none")]
815        pub min_properties: Option<usize>,
816    }
817}
818
819fn is_false(value: &bool) -> bool {
820    !*value
821}
822
823impl Object {
824    /// Initialize a new [`Object`] with default [`SchemaType`]. This effectively same as calling
825    /// `Object::with_type(SchemaType::Object)`.
826    pub fn new() -> Self {
827        Self {
828            ..Default::default()
829        }
830    }
831
832    /// Initialize new [`Object`] with given [`SchemaType`].
833    ///
834    /// Create [`std::string`] object type which can be used to define `string` field of an object.
835    /// ```rust
836    /// # use utoipa::openapi::schema::{Object, SchemaType};
837    /// let object = Object::with_type(SchemaType::String);
838    /// ```
839    pub fn with_type(schema_type: SchemaType) -> Self {
840        Self {
841            schema_type,
842            ..Default::default()
843        }
844    }
845}
846
847impl From<Object> for Schema {
848    fn from(s: Object) -> Self {
849        Self::Object(s)
850    }
851}
852
853impl ToArray for Object {}
854
855impl ObjectBuilder {
856    /// Add or change type of the object e.g [`SchemaType::String`].
857    pub fn schema_type(mut self, schema_type: SchemaType) -> Self {
858        set_value!(self schema_type schema_type)
859    }
860
861    /// Add or change additional format for detailing the schema type.
862    pub fn format(mut self, format: Option<SchemaFormat>) -> Self {
863        set_value!(self format format)
864    }
865
866    /// Add new property to the [`Object`].
867    ///
868    /// Method accepts property name and property component as an arguments.
869    pub fn property<S: Into<String>, I: Into<RefOr<Schema>>>(
870        mut self,
871        property_name: S,
872        component: I,
873    ) -> Self {
874        self.properties
875            .insert(property_name.into(), component.into());
876
877        self
878    }
879
880    pub fn additional_properties<I: Into<AdditionalProperties<Schema>>>(
881        mut self,
882        additional_properties: Option<I>,
883    ) -> Self {
884        set_value!(self additional_properties additional_properties.map(|additional_properties| Box::new(additional_properties.into())))
885    }
886
887    /// Add field to the required fields of [`Object`].
888    pub fn required<I: Into<String>>(mut self, required_field: I) -> Self {
889        self.required.push(required_field.into());
890
891        self
892    }
893
894    /// Add or change the title of the [`Object`].
895    pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
896        set_value!(self title title.map(|title| title.into()))
897    }
898
899    /// Add or change description of the property. Markdown syntax is supported.
900    pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
901        set_value!(self description description.map(|description| description.into()))
902    }
903
904    /// Add or change default value for the object which is provided when user has not provided the input in Swagger UI.
905    pub fn default(mut self, default: Option<Value>) -> Self {
906        set_value!(self default default)
907    }
908
909    /// Add or change deprecated status for [`Object`].
910    pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
911        set_value!(self deprecated deprecated)
912    }
913
914    /// Add or change enum property variants.
915    pub fn enum_values<I: IntoIterator<Item = E>, E: Into<Value>>(
916        mut self,
917        enum_values: Option<I>,
918    ) -> Self {
919        set_value!(self enum_values
920            enum_values.map(|values| values.into_iter().map(|enum_value| enum_value.into()).collect()))
921    }
922
923    /// Add or change example shown in UI of the value for richer documentation.
924    pub fn example(mut self, example: Option<Value>) -> Self {
925        set_value!(self example example)
926    }
927
928    /// Add or change write only flag for [`Object`].
929    pub fn write_only(mut self, write_only: Option<bool>) -> Self {
930        set_value!(self write_only write_only)
931    }
932
933    /// Add or change read only flag for [`Object`].
934    pub fn read_only(mut self, read_only: Option<bool>) -> Self {
935        set_value!(self read_only read_only)
936    }
937
938    /// Add or change additional [`Xml`] formatting of the [`Object`].
939    pub fn xml(mut self, xml: Option<Xml>) -> Self {
940        set_value!(self xml xml)
941    }
942
943    /// Add or change nullable flag for [`Object`].
944    pub fn nullable(mut self, nullable: bool) -> Self {
945        set_value!(self nullable nullable)
946    }
947
948    /// Set or change _`multiple_of`_ validation flag for `number` and `integer` type values.
949    pub fn multiple_of(mut self, multiple_of: Option<f64>) -> Self {
950        set_value!(self multiple_of multiple_of)
951    }
952
953    /// Set or change inclusive maximum value for `number` and `integer` values.
954    pub fn maximum(mut self, maximum: Option<f64>) -> Self {
955        set_value!(self maximum maximum)
956    }
957
958    /// Set or change inclusive minimum value for `number` and `integer` values.
959    pub fn minimum(mut self, minimum: Option<f64>) -> Self {
960        set_value!(self minimum minimum)
961    }
962
963    /// Set or change exclusive maximum value for `number` and `integer` values.
964    pub fn exclusive_maximum(mut self, exclusive_maximum: Option<f64>) -> Self {
965        set_value!(self exclusive_maximum exclusive_maximum)
966    }
967
968    /// Set or change exclusive minimum value for `number` and `integer` values.
969    pub fn exclusive_minimum(mut self, exclusive_minimum: Option<f64>) -> Self {
970        set_value!(self exclusive_minimum exclusive_minimum)
971    }
972
973    /// Set or change maximum length for `string` values.
974    pub fn max_length(mut self, max_length: Option<usize>) -> Self {
975        set_value!(self max_length max_length)
976    }
977
978    /// Set or change minimum length for `string` values.
979    pub fn min_length(mut self, min_length: Option<usize>) -> Self {
980        set_value!(self min_length min_length)
981    }
982
983    /// Set or change a valid regular expression for `string` value to match.
984    pub fn pattern<I: Into<String>>(mut self, pattern: Option<I>) -> Self {
985        set_value!(self pattern pattern.map(|pattern| pattern.into()))
986    }
987
988    /// Set or change maximum number of properties the [`Object`] can hold.
989    pub fn max_properties(mut self, max_properties: Option<usize>) -> Self {
990        set_value!(self max_properties max_properties)
991    }
992
993    /// Set or change minimum number of properties the [`Object`] can hold.
994    pub fn min_properties(mut self, min_properties: Option<usize>) -> Self {
995        set_value!(self min_properties min_properties)
996    }
997
998    to_array_builder!();
999}
1000
1001component_from_builder!(ObjectBuilder);
1002
1003impl From<ObjectBuilder> for RefOr<Schema> {
1004    fn from(builder: ObjectBuilder) -> Self {
1005        Self::T(Schema::Object(builder.build()))
1006    }
1007}
1008
1009/// AdditionalProperties is used to define values of map fields of the [`Schema`].
1010///
1011/// The value can either be [`RefOr`] or _`bool`_.
1012#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1013#[cfg_attr(feature = "debug", derive(Debug))]
1014#[serde(untagged)]
1015pub enum AdditionalProperties<T> {
1016    /// Use when value type of the map is a known [`Schema`] or [`Ref`] to the [`Schema`].
1017    RefOr(RefOr<T>),
1018    /// Use _`AdditionalProperties::FreeForm(true)`_ when any value is allowed in the map.
1019    FreeForm(bool),
1020}
1021
1022impl<T> From<RefOr<T>> for AdditionalProperties<T> {
1023    fn from(value: RefOr<T>) -> Self {
1024        Self::RefOr(value)
1025    }
1026}
1027
1028impl From<ObjectBuilder> for AdditionalProperties<Schema> {
1029    fn from(value: ObjectBuilder) -> Self {
1030        Self::RefOr(RefOr::T(Schema::Object(value.build())))
1031    }
1032}
1033
1034impl From<Ref> for AdditionalProperties<Schema> {
1035    fn from(value: Ref) -> Self {
1036        Self::RefOr(RefOr::Ref(value))
1037    }
1038}
1039
1040impl From<Schema> for AdditionalProperties<Schema> {
1041    fn from(value: Schema) -> Self {
1042        Self::RefOr(RefOr::T(value))
1043    }
1044}
1045
1046/// Implements [OpenAPI Reference Object][reference] that can be used to reference
1047/// reusable components such as [`Schema`]s or [`Response`]s.
1048///
1049/// [reference]: https://spec.openapis.org/oas/latest.html#reference-object
1050#[non_exhaustive]
1051#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
1052#[cfg_attr(feature = "debug", derive(Debug))]
1053pub struct Ref {
1054    /// Reference location of the actual component.
1055    #[serde(rename = "$ref")]
1056    pub ref_location: String,
1057}
1058
1059impl Ref {
1060    /// Construct a new [`Ref`] with custom ref location. In most cases this is not necessary
1061    /// and [`Ref::from_schema_name`] could be used instead.
1062    pub fn new<I: Into<String>>(ref_location: I) -> Self {
1063        Self {
1064            ref_location: ref_location.into(),
1065        }
1066    }
1067
1068    /// Construct a new [`Ref`] from provided schema name. This will create a [`Ref`] that
1069    /// references the the reusable schemas.
1070    pub fn from_schema_name<I: Into<String>>(schema_name: I) -> Self {
1071        Self::new(format!("#/components/schemas/{}", schema_name.into()))
1072    }
1073
1074    /// Construct a new [`Ref`] from provided response name. This will create a [`Ref`] that
1075    /// references the reusable response.
1076    pub fn from_response_name<I: Into<String>>(response_name: I) -> Self {
1077        Self::new(format!("#/components/responses/{}", response_name.into()))
1078    }
1079
1080    to_array_builder!();
1081}
1082
1083impl From<Ref> for RefOr<Schema> {
1084    fn from(r: Ref) -> Self {
1085        Self::Ref(r)
1086    }
1087}
1088
1089impl<T> From<T> for RefOr<T> {
1090    fn from(t: T) -> Self {
1091        Self::T(t)
1092    }
1093}
1094
1095impl Default for RefOr<Schema> {
1096    fn default() -> Self {
1097        Self::T(Schema::Object(Object::new()))
1098    }
1099}
1100
1101impl ToArray for RefOr<Schema> {}
1102
1103impl From<Object> for RefOr<Schema> {
1104    fn from(object: Object) -> Self {
1105        Self::T(Schema::Object(object))
1106    }
1107}
1108
1109impl From<Array> for RefOr<Schema> {
1110    fn from(array: Array) -> Self {
1111        Self::T(Schema::Array(array))
1112    }
1113}
1114
1115fn omit_decimal_zero<S>(maybe_value: &Option<f64>, s: S) -> Result<S::Ok, S::Error>
1116where
1117    S: serde::Serializer,
1118{
1119    if let Some(v) = maybe_value {
1120        if v.fract() == 0.0 && *v >= i64::MIN as f64 && *v <= i64::MAX as f64 {
1121            s.serialize_i64(v.trunc() as i64)
1122        } else {
1123            s.serialize_f64(*v)
1124        }
1125    } else {
1126        s.serialize_none()
1127    }
1128}
1129
1130builder! {
1131    ArrayBuilder;
1132
1133    /// Array represents [`Vec`] or [`slice`] type  of items.
1134    ///
1135    /// See [`Schema::Array`] for more details.
1136    #[non_exhaustive]
1137    #[derive(Serialize, Deserialize, Clone, PartialEq)]
1138    #[cfg_attr(feature = "debug", derive(Debug))]
1139    #[serde(rename_all = "camelCase")]
1140    pub struct Array {
1141        /// Type will always be [`SchemaType::Array`]
1142        #[serde(rename = "type")]
1143        pub schema_type: SchemaType,
1144
1145        /// Changes the [`Array`] title.
1146        #[serde(skip_serializing_if = "Option::is_none")]
1147        pub title: Option<String>,
1148
1149        /// Schema representing the array items type.
1150        pub items: Box<RefOr<Schema>>,
1151
1152        /// Description of the [`Array`]. Markdown syntax is supported.
1153        #[serde(skip_serializing_if = "Option::is_none")]
1154        pub description: Option<String>,
1155
1156        /// Marks the [`Array`] deprecated.
1157        #[serde(skip_serializing_if = "Option::is_none")]
1158        pub deprecated: Option<Deprecated>,
1159
1160        /// Example shown in UI of the value for richer documentation.
1161        #[serde(skip_serializing_if = "Option::is_none")]
1162        pub example: Option<Value>,
1163
1164        /// Default value which is provided when user has not provided the input in Swagger UI.
1165        #[serde(skip_serializing_if = "Option::is_none")]
1166        pub default: Option<Value>,
1167
1168        /// Max length of the array.
1169        #[serde(skip_serializing_if = "Option::is_none")]
1170        pub max_items: Option<usize>,
1171
1172        /// Min length of the array.
1173        #[serde(skip_serializing_if = "Option::is_none")]
1174        pub min_items: Option<usize>,
1175
1176        /// Setting this to `true` will validate successfully if all elements of this [`Array`] are
1177        /// unique.
1178        #[serde(default, skip_serializing_if = "is_false")]
1179        pub unique_items: bool,
1180
1181        /// Xml format of the array.
1182        #[serde(skip_serializing_if = "Option::is_none")]
1183        pub xml: Option<Xml>,
1184
1185        /// Set `true` to allow `"null"` to be used as value for given type.
1186        #[serde(default, skip_serializing_if = "is_false")]
1187        pub nullable: bool,
1188    }
1189}
1190
1191impl Default for Array {
1192    fn default() -> Self {
1193        Self {
1194            title: Default::default(),
1195            schema_type: SchemaType::Array,
1196            unique_items: bool::default(),
1197            items: Default::default(),
1198            description: Default::default(),
1199            deprecated: Default::default(),
1200            example: Default::default(),
1201            default: Default::default(),
1202            max_items: Default::default(),
1203            min_items: Default::default(),
1204            xml: Default::default(),
1205            nullable: Default::default(),
1206        }
1207    }
1208}
1209
1210impl Array {
1211    /// Construct a new [`Array`] component from given [`Schema`].
1212    ///
1213    /// # Examples
1214    ///
1215    /// Create a `String` array component.
1216    /// ```rust
1217    /// # use utoipa::openapi::schema::{Schema, Array, SchemaType, Object};
1218    /// let string_array = Array::new(Object::with_type(SchemaType::String));
1219    /// ```
1220    pub fn new<I: Into<RefOr<Schema>>>(component: I) -> Self {
1221        Self {
1222            items: Box::new(component.into()),
1223            ..Default::default()
1224        }
1225    }
1226}
1227
1228impl ArrayBuilder {
1229    /// Set [`Schema`] type for the [`Array`].
1230    pub fn items<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
1231        set_value!(self items Box::new(component.into()))
1232    }
1233
1234    /// Add or change the title of the [`Array`].
1235    pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
1236        set_value!(self title title.map(|title| title.into()))
1237    }
1238
1239    /// Add or change description of the property. Markdown syntax is supported.
1240    pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
1241        set_value!(self description description.map(|description| description.into()))
1242    }
1243
1244    /// Add or change deprecated status for [`Array`].
1245    pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
1246        set_value!(self deprecated deprecated)
1247    }
1248
1249    /// Add or change example shown in UI of the value for richer documentation.
1250    pub fn example(mut self, example: Option<Value>) -> Self {
1251        set_value!(self example example)
1252    }
1253
1254    /// Add or change default value for the object which is provided when user has not provided the input in Swagger UI.
1255    pub fn default(mut self, default: Option<Value>) -> Self {
1256        set_value!(self default default)
1257    }
1258
1259    /// Set maximum allowed length for [`Array`].
1260    pub fn max_items(mut self, max_items: Option<usize>) -> Self {
1261        set_value!(self max_items max_items)
1262    }
1263
1264    /// Set minimum allowed length for [`Array`].
1265    pub fn min_items(mut self, min_items: Option<usize>) -> Self {
1266        set_value!(self min_items min_items)
1267    }
1268
1269    /// Set or change whether [`Array`] should enforce all items to be unique.
1270    pub fn unique_items(mut self, unique_items: bool) -> Self {
1271        set_value!(self unique_items unique_items)
1272    }
1273
1274    /// Set [`Xml`] formatting for [`Array`].
1275    pub fn xml(mut self, xml: Option<Xml>) -> Self {
1276        set_value!(self xml xml)
1277    }
1278
1279    /// Add or change nullable flag for [`Object`].
1280    pub fn nullable(mut self, nullable: bool) -> Self {
1281        set_value!(self nullable nullable)
1282    }
1283
1284    to_array_builder!();
1285}
1286
1287component_from_builder!(ArrayBuilder);
1288
1289impl From<Array> for Schema {
1290    fn from(array: Array) -> Self {
1291        Self::Array(array)
1292    }
1293}
1294
1295impl From<ArrayBuilder> for RefOr<Schema> {
1296    fn from(array: ArrayBuilder) -> Self {
1297        Self::T(Schema::Array(array.build()))
1298    }
1299}
1300
1301impl ToArray for Array {}
1302
1303pub trait ToArray
1304where
1305    RefOr<Schema>: From<Self>,
1306    Self: Sized,
1307{
1308    fn to_array(self) -> Array {
1309        Array::new(self)
1310    }
1311}
1312
1313/// Represents data type of [`Schema`].
1314#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1315#[cfg_attr(feature = "debug", derive(Debug))]
1316#[serde(rename_all = "lowercase")]
1317pub enum SchemaType {
1318    /// Used with [`Object`] and [`ObjectBuilder`]. Objects always have
1319    /// _schema_type_ [`SchemaType::Object`].
1320    Object,
1321    /// Indicates generic JSON content. Used with [`Object`] and [`ObjectBuilder`] on a field
1322    /// of any valid JSON type.
1323    Value,
1324    /// Indicates string type of content. Used with [`Object`] and [`ObjectBuilder`] on a `string`
1325    /// field.
1326    String,
1327    /// Indicates integer type of content. Used with [`Object`] and [`ObjectBuilder`] on a `number`
1328    /// field.
1329    Integer,
1330    /// Indicates floating point number type of content. Used with
1331    /// [`Object`] and [`ObjectBuilder`] on a `number` field.
1332    Number,
1333    /// Indicates boolean type of content. Used with [`Object`] and [`ObjectBuilder`] on
1334    /// a `bool` field.
1335    Boolean,
1336    /// Used with [`Array`] and [`ArrayBuilder`]. Indicates array type of content.
1337    Array,
1338}
1339impl SchemaType {
1340    fn is_value(type_: &SchemaType) -> bool {
1341        *type_ == SchemaType::Value
1342    }
1343}
1344
1345impl Default for SchemaType {
1346    fn default() -> Self {
1347        Self::Object
1348    }
1349}
1350
1351/// Additional format for [`SchemaType`] to fine tune the data type used. If the **format** is not
1352/// supported by the UI it may default back to [`SchemaType`] alone.
1353/// Format is an open value, so you can use any formats, even not those defined by the
1354/// OpenAPI Specification.
1355#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1356#[cfg_attr(feature = "debug", derive(Debug))]
1357#[serde(rename_all = "lowercase", untagged)]
1358pub enum SchemaFormat {
1359    /// Use to define additional detail about the value.
1360    KnownFormat(KnownFormat),
1361    /// Can be used to provide additional detail about the value when [`SchemaFormat::KnownFormat`]
1362    /// is not suitable.
1363    Custom(String),
1364}
1365
1366/// Known schema format modifier property to provide fine detail of the primitive type.
1367#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1368#[cfg_attr(feature = "debug", derive(Debug))]
1369#[serde(rename_all = "lowercase")]
1370pub enum KnownFormat {
1371    /// 8 bit integer.
1372    #[cfg(feature = "non_strict_integers")]
1373    #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
1374    Int8,
1375    /// 16 bit integer.
1376    #[cfg(feature = "non_strict_integers")]
1377    #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
1378    Int16,
1379    /// 32 bit integer.
1380    Int32,
1381    /// 64 bit integer.
1382    Int64,
1383    /// 8 bit unsigned integer.
1384    #[cfg(feature = "non_strict_integers")]
1385    #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
1386    UInt8,
1387    /// 16 bit unsigned integer.
1388    #[cfg(feature = "non_strict_integers")]
1389    #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
1390    UInt16,
1391    /// 32 bit unsigned integer.
1392    #[cfg(feature = "non_strict_integers")]
1393    #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
1394    UInt32,
1395    /// 64 bit unsigned integer.
1396    #[cfg(feature = "non_strict_integers")]
1397    #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
1398    UInt64,
1399    /// floating point number.
1400    Float,
1401    /// double (floating point) number.
1402    Double,
1403    /// base64 encoded chars.
1404    Byte,
1405    /// binary data (octet).
1406    Binary,
1407    /// ISO-8601 full date [FRC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14).
1408    Date,
1409    /// ISO-8601 full date time [FRC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14).
1410    #[serde(rename = "date-time")]
1411    DateTime,
1412    /// Hint to UI to obscure input.
1413    Password,
1414    /// Used with [`String`] values to indicate value is in UUID format.
1415    ///
1416    /// **uuid** feature need to be enabled.
1417    #[cfg(feature = "uuid")]
1418    #[cfg_attr(doc_cfg, doc(cfg(feature = "uuid")))]
1419    Uuid,
1420    /// Used with [`String`] values to indicate value is in ULID format.
1421    ///
1422    /// **ulid** feature need to be enabled.
1423    #[cfg(feature = "ulid")]
1424    #[cfg_attr(doc_cfg, doc(cfg(feature = "ulid")))]
1425    Ulid,
1426}
1427
1428#[cfg(test)]
1429mod tests {
1430    use assert_json_diff::assert_json_eq;
1431    use serde_json::{json, Value};
1432
1433    use super::*;
1434    use crate::openapi::*;
1435
1436    #[test]
1437    fn create_schema_serializes_json() -> Result<(), serde_json::Error> {
1438        let openapi = OpenApiBuilder::new()
1439            .info(Info::new("My api", "1.0.0"))
1440            .paths(Paths::new())
1441            .components(Some(
1442                ComponentsBuilder::new()
1443                    .schema("Person", Ref::new("#/components/PersonModel"))
1444                    .schema(
1445                        "Credential",
1446                        Schema::from(
1447                            ObjectBuilder::new()
1448                                .property(
1449                                    "id",
1450                                    ObjectBuilder::new()
1451                                        .schema_type(SchemaType::Integer)
1452                                        .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
1453                                        .description(Some("Id of credential"))
1454                                        .default(Some(json!(1i32))),
1455                                )
1456                                .property(
1457                                    "name",
1458                                    ObjectBuilder::new()
1459                                        .schema_type(SchemaType::String)
1460                                        .description(Some("Name of credential")),
1461                                )
1462                                .property(
1463                                    "status",
1464                                    ObjectBuilder::new()
1465                                        .schema_type(SchemaType::String)
1466                                        .default(Some(json!("Active")))
1467                                        .description(Some("Credential status"))
1468                                        .enum_values(Some([
1469                                            "Active",
1470                                            "NotActive",
1471                                            "Locked",
1472                                            "Expired",
1473                                        ])),
1474                                )
1475                                .property(
1476                                    "history",
1477                                    Array::new(Ref::from_schema_name("UpdateHistory")),
1478                                )
1479                                .property("tags", Object::with_type(SchemaType::String).to_array()),
1480                        ),
1481                    )
1482                    .build(),
1483            ))
1484            .build();
1485
1486        let serialized = serde_json::to_string_pretty(&openapi)?;
1487        println!("serialized json:\n {serialized}");
1488
1489        let value = serde_json::to_value(&openapi)?;
1490        let credential = get_json_path(&value, "components.schemas.Credential.properties");
1491        let person = get_json_path(&value, "components.schemas.Person");
1492
1493        assert!(
1494            credential.get("id").is_some(),
1495            "could not find path: components.schemas.Credential.properties.id"
1496        );
1497        assert!(
1498            credential.get("status").is_some(),
1499            "could not find path: components.schemas.Credential.properties.status"
1500        );
1501        assert!(
1502            credential.get("name").is_some(),
1503            "could not find path: components.schemas.Credential.properties.name"
1504        );
1505        assert!(
1506            credential.get("history").is_some(),
1507            "could not find path: components.schemas.Credential.properties.history"
1508        );
1509        assert_eq!(
1510            credential
1511                .get("id")
1512                .unwrap_or(&serde_json::value::Value::Null)
1513                .to_string(),
1514            r#"{"default":1,"description":"Id of credential","format":"int32","type":"integer"}"#,
1515            "components.schemas.Credential.properties.id did not match"
1516        );
1517        assert_eq!(
1518            credential
1519                .get("name")
1520                .unwrap_or(&serde_json::value::Value::Null)
1521                .to_string(),
1522            r#"{"description":"Name of credential","type":"string"}"#,
1523            "components.schemas.Credential.properties.name did not match"
1524        );
1525        assert_eq!(
1526            credential
1527                .get("status")
1528                .unwrap_or(&serde_json::value::Value::Null)
1529                .to_string(),
1530            r#"{"default":"Active","description":"Credential status","enum":["Active","NotActive","Locked","Expired"],"type":"string"}"#,
1531            "components.schemas.Credential.properties.status did not match"
1532        );
1533        assert_eq!(
1534            credential
1535                .get("history")
1536                .unwrap_or(&serde_json::value::Value::Null)
1537                .to_string(),
1538            r###"{"items":{"$ref":"#/components/schemas/UpdateHistory"},"type":"array"}"###,
1539            "components.schemas.Credential.properties.history did not match"
1540        );
1541        assert_eq!(
1542            person.to_string(),
1543            r###"{"$ref":"#/components/PersonModel"}"###,
1544            "components.schemas.Person.ref did not match"
1545        );
1546
1547        Ok(())
1548    }
1549
1550    // Examples taken from https://spec.openapis.org/oas/latest.html#model-with-map-dictionary-properties
1551    #[test]
1552    fn test_property_order() {
1553        let json_value = ObjectBuilder::new()
1554            .property(
1555                "id",
1556                ObjectBuilder::new()
1557                    .schema_type(SchemaType::Integer)
1558                    .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
1559                    .description(Some("Id of credential"))
1560                    .default(Some(json!(1i32))),
1561            )
1562            .property(
1563                "name",
1564                ObjectBuilder::new()
1565                    .schema_type(SchemaType::String)
1566                    .description(Some("Name of credential")),
1567            )
1568            .property(
1569                "status",
1570                ObjectBuilder::new()
1571                    .schema_type(SchemaType::String)
1572                    .default(Some(json!("Active")))
1573                    .description(Some("Credential status"))
1574                    .enum_values(Some(["Active", "NotActive", "Locked", "Expired"])),
1575            )
1576            .property(
1577                "history",
1578                Array::new(Ref::from_schema_name("UpdateHistory")),
1579            )
1580            .property("tags", Object::with_type(SchemaType::String).to_array())
1581            .build();
1582
1583        #[cfg(not(feature = "preserve_order"))]
1584        assert_eq!(
1585            json_value.properties.keys().collect::<Vec<_>>(),
1586            vec!["history", "id", "name", "status", "tags"]
1587        );
1588
1589        #[cfg(feature = "preserve_order")]
1590        assert_eq!(
1591            json_value.properties.keys().collect::<Vec<_>>(),
1592            vec!["id", "name", "status", "history", "tags"]
1593        );
1594    }
1595
1596    // Examples taken from https://spec.openapis.org/oas/latest.html#model-with-map-dictionary-properties
1597    #[test]
1598    fn test_additional_properties() {
1599        let json_value = ObjectBuilder::new()
1600            .additional_properties(Some(ObjectBuilder::new().schema_type(SchemaType::String)))
1601            .build();
1602        assert_json_eq!(
1603            json_value,
1604            json!({
1605                "type": "object",
1606                "additionalProperties": {
1607                    "type": "string"
1608                }
1609            })
1610        );
1611
1612        let json_value = ObjectBuilder::new()
1613            .additional_properties(Some(Ref::from_schema_name("ComplexModel")))
1614            .build();
1615        assert_json_eq!(
1616            json_value,
1617            json!({
1618                "type": "object",
1619                "additionalProperties": {
1620                    "$ref": "#/components/schemas/ComplexModel"
1621                }
1622            })
1623        )
1624    }
1625
1626    #[test]
1627    fn test_object_with_title() {
1628        let json_value = ObjectBuilder::new().title(Some("SomeName")).build();
1629        assert_json_eq!(
1630            json_value,
1631            json!({
1632                "type": "object",
1633                "title": "SomeName"
1634            })
1635        );
1636    }
1637
1638    #[test]
1639    fn derive_object_with_example() {
1640        let expected = r#"{"type":"object","example":{"age":20,"name":"bob the cat"}}"#;
1641        let json_value = ObjectBuilder::new()
1642            .example(Some(json!({"age": 20, "name": "bob the cat"})))
1643            .build();
1644
1645        let value_string = serde_json::to_string(&json_value).unwrap();
1646        assert_eq!(
1647            value_string, expected,
1648            "value string != expected string, {value_string} != {expected}"
1649        );
1650    }
1651
1652    fn get_json_path<'a>(value: &'a Value, path: &str) -> &'a Value {
1653        path.split('.').fold(value, |acc, fragment| {
1654            acc.get(fragment).unwrap_or(&serde_json::value::Value::Null)
1655        })
1656    }
1657
1658    #[test]
1659    fn test_array_new() {
1660        let array = Array::new(
1661            ObjectBuilder::new().property(
1662                "id",
1663                ObjectBuilder::new()
1664                    .schema_type(SchemaType::Integer)
1665                    .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
1666                    .description(Some("Id of credential"))
1667                    .default(Some(json!(1i32))),
1668            ),
1669        );
1670
1671        assert!(matches!(array.schema_type, SchemaType::Array));
1672    }
1673
1674    #[test]
1675    fn test_array_builder() {
1676        let array: Array = ArrayBuilder::new()
1677            .items(
1678                ObjectBuilder::new().property(
1679                    "id",
1680                    ObjectBuilder::new()
1681                        .schema_type(SchemaType::Integer)
1682                        .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
1683                        .description(Some("Id of credential"))
1684                        .default(Some(json!(1i32))),
1685                ),
1686            )
1687            .build();
1688
1689        assert!(matches!(array.schema_type, SchemaType::Array));
1690    }
1691
1692    #[test]
1693    fn reserialize_deserialized_schema_components() {
1694        let components = ComponentsBuilder::new()
1695            .schemas_from_iter(vec![(
1696                "Comp",
1697                Schema::from(
1698                    ObjectBuilder::new()
1699                        .property("name", ObjectBuilder::new().schema_type(SchemaType::String))
1700                        .required("name"),
1701                ),
1702            )])
1703            .responses_from_iter(vec![(
1704                "200",
1705                ResponseBuilder::new().description("Okay").build(),
1706            )])
1707            .security_scheme("TLS", SecurityScheme::MutualTls { description: None })
1708            .build();
1709
1710        let serialized_components = serde_json::to_string(&components).unwrap();
1711
1712        let deserialized_components: Components =
1713            serde_json::from_str(serialized_components.as_str()).unwrap();
1714
1715        assert_eq!(
1716            serialized_components,
1717            serde_json::to_string(&deserialized_components).unwrap()
1718        )
1719    }
1720
1721    #[test]
1722    fn reserialize_deserialized_object_component() {
1723        let prop = ObjectBuilder::new()
1724            .property("name", ObjectBuilder::new().schema_type(SchemaType::String))
1725            .required("name")
1726            .build();
1727
1728        let serialized_components = serde_json::to_string(&prop).unwrap();
1729        let deserialized_components: Object =
1730            serde_json::from_str(serialized_components.as_str()).unwrap();
1731
1732        assert_eq!(
1733            serialized_components,
1734            serde_json::to_string(&deserialized_components).unwrap()
1735        )
1736    }
1737
1738    #[test]
1739    fn reserialize_deserialized_property() {
1740        let prop = ObjectBuilder::new().schema_type(SchemaType::String).build();
1741
1742        let serialized_components = serde_json::to_string(&prop).unwrap();
1743        let deserialized_components: Object =
1744            serde_json::from_str(serialized_components.as_str()).unwrap();
1745
1746        assert_eq!(
1747            serialized_components,
1748            serde_json::to_string(&deserialized_components).unwrap()
1749        )
1750    }
1751
1752    #[test]
1753    fn serialize_deserialize_array_within_ref_or_t_object_builder() {
1754        let ref_or_schema = RefOr::T(Schema::Object(
1755            ObjectBuilder::new()
1756                .property(
1757                    "test",
1758                    RefOr::T(Schema::Array(
1759                        ArrayBuilder::new()
1760                            .items(RefOr::T(Schema::Object(
1761                                ObjectBuilder::new()
1762                                    .property("element", RefOr::Ref(Ref::new("#/test")))
1763                                    .build(),
1764                            )))
1765                            .build(),
1766                    )),
1767                )
1768                .build(),
1769        ));
1770
1771        let json_str = serde_json::to_string(&ref_or_schema).expect("");
1772        println!("----------------------------");
1773        println!("{json_str}");
1774
1775        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1776
1777        let json_de_str = serde_json::to_string(&deserialized).expect("");
1778        println!("----------------------------");
1779        println!("{json_de_str}");
1780
1781        assert_eq!(json_str, json_de_str);
1782    }
1783
1784    #[test]
1785    fn serialize_deserialize_one_of_within_ref_or_t_object_builder() {
1786        let ref_or_schema = RefOr::T(Schema::Object(
1787            ObjectBuilder::new()
1788                .property(
1789                    "test",
1790                    RefOr::T(Schema::OneOf(
1791                        OneOfBuilder::new()
1792                            .item(Schema::Array(
1793                                ArrayBuilder::new()
1794                                    .items(RefOr::T(Schema::Object(
1795                                        ObjectBuilder::new()
1796                                            .property("element", RefOr::Ref(Ref::new("#/test")))
1797                                            .build(),
1798                                    )))
1799                                    .build(),
1800                            ))
1801                            .item(Schema::Array(
1802                                ArrayBuilder::new()
1803                                    .items(RefOr::T(Schema::Object(
1804                                        ObjectBuilder::new()
1805                                            .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
1806                                            .build(),
1807                                    )))
1808                                    .build(),
1809                            ))
1810                            .build(),
1811                    )),
1812                )
1813                .build(),
1814        ));
1815
1816        let json_str = serde_json::to_string(&ref_or_schema).expect("");
1817        println!("----------------------------");
1818        println!("{json_str}");
1819
1820        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1821
1822        let json_de_str = serde_json::to_string(&deserialized).expect("");
1823        println!("----------------------------");
1824        println!("{json_de_str}");
1825
1826        assert_eq!(json_str, json_de_str);
1827    }
1828
1829    #[test]
1830    fn serialize_deserialize_all_of_of_within_ref_or_t_object_builder() {
1831        let ref_or_schema = RefOr::T(Schema::Object(
1832            ObjectBuilder::new()
1833                .property(
1834                    "test",
1835                    RefOr::T(Schema::AllOf(
1836                        AllOfBuilder::new()
1837                            .item(Schema::Array(
1838                                ArrayBuilder::new()
1839                                    .items(RefOr::T(Schema::Object(
1840                                        ObjectBuilder::new()
1841                                            .property("element", RefOr::Ref(Ref::new("#/test")))
1842                                            .build(),
1843                                    )))
1844                                    .build(),
1845                            ))
1846                            .item(RefOr::T(Schema::Object(
1847                                ObjectBuilder::new()
1848                                    .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
1849                                    .build(),
1850                            )))
1851                            .build(),
1852                    )),
1853                )
1854                .build(),
1855        ));
1856
1857        let json_str = serde_json::to_string(&ref_or_schema).expect("");
1858        println!("----------------------------");
1859        println!("{json_str}");
1860
1861        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1862
1863        let json_de_str = serde_json::to_string(&deserialized).expect("");
1864        println!("----------------------------");
1865        println!("{json_de_str}");
1866
1867        assert_eq!(json_str, json_de_str);
1868    }
1869
1870    #[test]
1871    fn serialize_deserialize_any_of_of_within_ref_or_t_object_builder() {
1872        let ref_or_schema = RefOr::T(Schema::Object(
1873            ObjectBuilder::new()
1874                .property(
1875                    "test",
1876                    RefOr::T(Schema::AnyOf(
1877                        AnyOfBuilder::new()
1878                            .item(Schema::Array(
1879                                ArrayBuilder::new()
1880                                    .items(RefOr::T(Schema::Object(
1881                                        ObjectBuilder::new()
1882                                            .property("element", RefOr::Ref(Ref::new("#/test")))
1883                                            .build(),
1884                                    )))
1885                                    .build(),
1886                            ))
1887                            .item(RefOr::T(Schema::Object(
1888                                ObjectBuilder::new()
1889                                    .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
1890                                    .build(),
1891                            )))
1892                            .build(),
1893                    )),
1894                )
1895                .build(),
1896        ));
1897
1898        let json_str = serde_json::to_string(&ref_or_schema).expect("");
1899        println!("----------------------------");
1900        println!("{json_str}");
1901
1902        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1903
1904        let json_de_str = serde_json::to_string(&deserialized).expect("");
1905        println!("----------------------------");
1906        println!("{json_de_str}");
1907        assert!(json_str.contains("\"anyOf\""));
1908        assert_eq!(json_str, json_de_str);
1909    }
1910
1911    #[test]
1912    fn serialize_deserialize_schema_array_ref_or_t() {
1913        let ref_or_schema = RefOr::T(Schema::Array(
1914            ArrayBuilder::new()
1915                .items(RefOr::T(Schema::Object(
1916                    ObjectBuilder::new()
1917                        .property("element", RefOr::Ref(Ref::new("#/test")))
1918                        .build(),
1919                )))
1920                .build(),
1921        ));
1922
1923        let json_str = serde_json::to_string(&ref_or_schema).expect("");
1924        println!("----------------------------");
1925        println!("{json_str}");
1926
1927        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1928
1929        let json_de_str = serde_json::to_string(&deserialized).expect("");
1930        println!("----------------------------");
1931        println!("{json_de_str}");
1932
1933        assert_eq!(json_str, json_de_str);
1934    }
1935
1936    #[test]
1937    fn serialize_deserialize_schema_array_builder() {
1938        let ref_or_schema = ArrayBuilder::new()
1939            .items(RefOr::T(Schema::Object(
1940                ObjectBuilder::new()
1941                    .property("element", RefOr::Ref(Ref::new("#/test")))
1942                    .build(),
1943            )))
1944            .build();
1945
1946        let json_str = serde_json::to_string(&ref_or_schema).expect("");
1947        println!("----------------------------");
1948        println!("{json_str}");
1949
1950        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1951
1952        let json_de_str = serde_json::to_string(&deserialized).expect("");
1953        println!("----------------------------");
1954        println!("{json_de_str}");
1955
1956        assert_eq!(json_str, json_de_str);
1957    }
1958
1959    #[test]
1960    fn serialize_deserialize_schema_with_additional_properties() {
1961        let schema = Schema::Object(
1962            ObjectBuilder::new()
1963                .property(
1964                    "map",
1965                    ObjectBuilder::new()
1966                        .additional_properties(Some(AdditionalProperties::FreeForm(true))),
1967                )
1968                .build(),
1969        );
1970
1971        let json_str = serde_json::to_string(&schema).unwrap();
1972        println!("----------------------------");
1973        println!("{json_str}");
1974
1975        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1976
1977        let json_de_str = serde_json::to_string(&deserialized).unwrap();
1978        println!("----------------------------");
1979        println!("{json_de_str}");
1980
1981        assert_eq!(json_str, json_de_str);
1982    }
1983
1984    #[test]
1985    fn serialize_deserialize_schema_with_additional_properties_object() {
1986        let schema = Schema::Object(
1987            ObjectBuilder::new()
1988                .property(
1989                    "map",
1990                    ObjectBuilder::new().additional_properties(Some(
1991                        ObjectBuilder::new()
1992                            .property("name", Object::with_type(SchemaType::String)),
1993                    )),
1994                )
1995                .build(),
1996        );
1997
1998        let json_str = serde_json::to_string(&schema).unwrap();
1999        println!("----------------------------");
2000        println!("{json_str}");
2001
2002        let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
2003
2004        let json_de_str = serde_json::to_string(&deserialized).unwrap();
2005        println!("----------------------------");
2006        println!("{json_de_str}");
2007
2008        assert_eq!(json_str, json_de_str);
2009    }
2010}