Skip to main content

autapi/openapi/
schema.rs

1use std::{fmt, mem::swap, ops::Deref, slice};
2
3use serde::{Deserialize, Deserializer, Serialize, de::Visitor};
4use serde_json::{Number as JsonNumber, Value};
5use serde_value::ValueDeserializer;
6use serde_with::skip_serializing_none;
7
8use crate::openapi::{Map, MaybeRef, macros};
9
10macros::define_openapi_spec_object! {
11    [override_with]:
12    #[derive(Default)]
13    pub struct Schema {
14        // -- Validation for any instance type
15        #[serde(rename = "type")]
16        pub schema_type: Option<SchemaType>,
17        #[serde(rename = "enum")]
18        #[serde(skip_serializing_if = "Vec::is_empty")]
19        pub enum_values: Vec<Value>,
20        #[serde(rename = "const")]
21        pub const_value: Option<Value>,
22        pub format: Option<Format>,
23
24        // -- Validation for objects
25        #[serde(skip_serializing_if = "Map::is_empty")]
26        pub properties: Map<String, MaybeRef<Schema>>,
27        pub property_names: Option<Box<MaybeRef<Schema>>>,
28        pub additional_properties: Option<Box<MaybeRef<Schema>>>,
29        pub max_properties: Option<u64>,
30        pub min_properties: Option<u64>,
31        #[serde(skip_serializing_if = "Vec::is_empty")]
32        pub required: Vec<String>,
33        #[serde(skip_serializing_if = "Map::is_empty")]
34        pub dependent_required: Map<String, Vec<String>>,
35
36        // -- Validation for numbers
37        pub multiple_of: Option<JsonNumber>,
38        pub maximum: Option<JsonNumber>,
39        pub exclusive_maximum: Option<JsonNumber>,
40        pub minimum: Option<JsonNumber>,
41        pub exclusive_minimum: Option<JsonNumber>,
42
43        // -- Validation for strings
44        pub max_length: Option<u64>,
45        pub min_length: Option<u64>,
46        pub pattern: Option<String>,
47        pub content_encoding: Option<String>,
48        pub content_media_type: Option<String>,
49        pub content_schema: Option<Box<MaybeRef<Schema>>>,
50
51        // -- Validation for arrays
52        pub items: Option<ArrayItems>,
53        #[serde(skip_serializing_if = "Vec::is_empty")]
54        pub prefix_items: Vec<MaybeRef<Schema>>,
55
56        pub max_items: Option<u64>,
57        pub min_items: Option<u64>,
58        pub unique_items: Option<bool>,
59        pub max_contains: Option<u64>,
60        pub min_contains: Option<u64>,
61
62        // -- Meta information
63        pub title: Option<String>,
64        pub description: Option<String>,
65        pub default: Option<Value>,
66        pub deprecated: Option<bool>,
67        pub read_only: Option<bool>,
68        pub write_only: Option<bool>,
69        pub example: Option<Value>,
70        #[serde(skip_serializing_if = "Vec::is_empty")]
71        pub examples: Vec<Value>,
72
73        // -- Polymorphism
74        #[serde(skip_serializing_if = "Vec::is_empty")]
75        pub all_of: Vec<MaybeRef<Schema>>,
76        #[serde(skip_serializing_if = "Vec::is_empty")]
77        pub one_of: Vec<MaybeRef<Schema>>,
78        #[serde(skip_serializing_if = "Vec::is_empty")]
79        pub any_of: Vec<MaybeRef<Schema>>,
80        pub discriminator: Option<Discriminator>,
81    }
82}
83
84impl Schema {
85    pub fn new_string_constant(string: impl Into<String>) -> Self {
86        Schema::default()
87            .with_schema_type(Type::String)
88            .with_enum_values(vec![Value::String(string.into())])
89    }
90    pub fn null() -> Self {
91        Schema::default().with_schema_type(Type::Null)
92    }
93}
94
95#[derive(PartialEq, Eq, Clone)]
96pub enum ArrayItems {
97    False,
98    Schema(Box<MaybeRef<Schema>>),
99}
100
101impl<I: Into<MaybeRef<Schema>>> From<I> for ArrayItems {
102    fn from(value: I) -> Self {
103        Self::Schema(Box::new(value.into()))
104    }
105}
106
107impl Serialize for ArrayItems {
108    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
109    where
110        S: serde::Serializer,
111    {
112        match self {
113            Self::False => serializer.serialize_bool(false),
114            Self::Schema(schema) => schema.serialize(serializer),
115        }
116    }
117}
118
119impl<'de> Deserialize<'de> for ArrayItems {
120    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
121    where
122        D: serde::Deserializer<'de>,
123    {
124        struct Vis;
125        impl<'de> Visitor<'de> for Vis {
126            type Value = ArrayItems;
127
128            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
129                formatter.write_str("false")
130            }
131
132            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
133            where
134                E: serde::de::Error,
135            {
136                if !v {
137                    Ok(ArrayItems::False)
138                } else {
139                    Err(E::custom("array items must be false or a schema object"))
140                }
141            }
142        }
143        let value = deserializer.deserialize_any(serde_value::ValueVisitor)?;
144        if let Ok(schema) = Box::deserialize(ValueDeserializer::<D::Error>::new(value.clone())) {
145            return Ok(Self::Schema(schema));
146        }
147        ValueDeserializer::<D::Error>::new(value).deserialize_bool(Vis)
148    }
149}
150
151#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
152#[serde(untagged)]
153pub enum SchemaType {
154    Single(Type),
155    Multiple(Vec<Type>),
156}
157
158impl SchemaType {
159    pub fn push(&mut self, ty: Type) {
160        match self {
161            Self::Single(prev_ty) => {
162                let mut swap_target = Type::Null;
163                swap(prev_ty, &mut swap_target);
164                *self = Self::Multiple(vec![swap_target, ty])
165            }
166            Self::Multiple(prev_types) => prev_types.push(ty),
167        }
168    }
169}
170
171impl Deref for SchemaType {
172    type Target = [Type];
173
174    fn deref(&self) -> &Self::Target {
175        match self {
176            Self::Single(single) => slice::from_ref(single),
177            Self::Multiple(multiple) => multiple,
178        }
179    }
180}
181
182impl From<Type> for SchemaType {
183    fn from(value: Type) -> Self {
184        Self::Single(value)
185    }
186}
187
188#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
189#[serde(rename_all = "camelCase")]
190#[non_exhaustive]
191pub enum Type {
192    Null,
193    Boolean,
194    Object,
195    Array,
196    Number,
197    String,
198    Integer,
199}
200
201/// A subset of the [OpenAPI Format Registry](https://spec.openapis.org/registry/format/)
202#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
203#[serde(rename_all = "kebab-case")]
204#[non_exhaustive]
205pub enum Format {
206    // -- Primitive data types --
207    Int8,
208    Int16,
209    Int32,
210    Int64,
211    Uint8,
212    Uint16,
213    Uint32,
214    Uint64,
215    Float,
216    Double,
217    /// A single character
218    Char,
219    /// Any sequence of octets
220    Binary,
221
222    // -- string formats --
223    /// RFC3339 date-time without the timezone component
224    DateTimeLocal,
225    /// Date and time as defined by date-time - RFC3339
226    DateTime,
227    /// Date as defined by full-date - RFC3339
228    Date,
229    /// Time as defined by full-time - RFC3339
230    Time,
231    /// Duration as defined by duration - RFC3339
232    Duration,
233    /// Base64 encoded data as defined in RFC4648
234    Byte,
235    /// Binary data encoded as a url-safe string as defined in RFC4648
236    Base64Url,
237    /// Commonmark-formatted text
238    Commonmark,
239    /// A fixed point decimal number of unspecified precision and range
240    Decimal,
241
242    Email,
243    IdnEmail,
244
245    Hostname,
246    IdnHostname,
247
248    IPv4,
249    IPv6,
250
251    Uri,
252    UriReference,
253    Iri,
254    IriReference,
255    Uuid,
256
257    UriTemplate,
258
259    JsonPointer,
260    RelativeJsonPointer,
261
262    Regex,
263
264    #[serde(untagged)]
265    Other(String),
266}
267
268#[skip_serializing_none]
269#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
270#[serde(rename_all = "camelCase")]
271#[non_exhaustive]
272pub struct Discriminator {
273    pub property_name: String,
274    #[serde(skip_serializing_if = "Map::is_empty")]
275    pub mapping: Map<String, String>,
276    pub default_mapping: Option<String>,
277}
278
279impl Discriminator {
280    pub fn new(property_name: String) -> Self {
281        Self {
282            property_name,
283            mapping: Default::default(),
284            default_mapping: Default::default(),
285        }
286    }
287}