1use crate::*;
2use indexmap::IndexMap;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
6#[serde(rename_all = "camelCase")]
7pub struct SchemaData {
8 #[serde(default, skip_serializing_if = "is_false")]
9 pub nullable: bool,
10 #[serde(default, skip_serializing_if = "is_false")]
11 pub read_only: bool,
12 #[serde(default, skip_serializing_if = "is_false")]
13 pub write_only: bool,
14 #[serde(default, skip_serializing_if = "is_false")]
15 pub deprecated: bool,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub external_docs: Option<ExternalDocumentation>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub example: Option<serde_json::Value>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub title: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub description: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub discriminator: Option<Discriminator>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub default: Option<serde_json::Value>,
28 #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
30 pub extensions: IndexMap<String, serde_json::Value>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct Schema {
35 #[serde(flatten)]
36 pub schema_data: SchemaData,
37 #[serde(flatten)]
38 pub schema_kind: SchemaKind,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(untagged)]
43pub enum SchemaKind {
44 Type(Type),
45 OneOf {
46 #[serde(rename = "oneOf")]
47 one_of: Vec<ReferenceOr<Schema>>,
48 },
49 AllOf {
50 #[serde(rename = "allOf")]
51 all_of: Vec<ReferenceOr<Schema>>,
52 },
53 AnyOf {
54 #[serde(rename = "anyOf")]
55 any_of: Vec<ReferenceOr<Schema>>,
56 },
57 Not {
58 not: Box<ReferenceOr<Schema>>,
59 },
60 Any(AnySchema),
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
64#[serde(tag = "type", rename_all = "lowercase")]
65pub enum Type {
66 String(StringType),
67 Number(NumberType),
68 Integer(IntegerType),
69 Object(ObjectType),
70 Array(ArrayType),
71 Boolean {},
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75#[serde(untagged)]
76pub enum AdditionalProperties {
77 Any(bool),
78 Schema(Box<ReferenceOr<Schema>>),
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
84#[serde(rename_all = "camelCase")]
85pub struct AnySchema {
86 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
87 pub typ: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub pattern: Option<String>,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub multiple_of: Option<f64>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub exclusive_minimum: Option<bool>,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub exclusive_maximum: Option<bool>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub minimum: Option<f64>,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub maximum: Option<f64>,
100 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
101 pub properties: IndexMap<String, ReferenceOr<Box<Schema>>>,
102 #[serde(default, skip_serializing_if = "Vec::is_empty")]
103 pub required: Vec<String>,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub additional_properties: Option<AdditionalProperties>,
106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub min_properties: Option<usize>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub max_properties: Option<usize>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub items: Option<ReferenceOr<Box<Schema>>>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub min_items: Option<usize>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub max_items: Option<usize>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub unique_items: Option<bool>,
118 #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
119 pub enumeration: Vec<serde_json::Value>,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub format: Option<String>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub min_length: Option<usize>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub max_length: Option<usize>,
126 #[serde(default, skip_serializing_if = "Vec::is_empty")]
127 pub one_of: Vec<ReferenceOr<Schema>>,
128 #[serde(default, skip_serializing_if = "Vec::is_empty")]
129 pub all_of: Vec<ReferenceOr<Schema>>,
130 #[serde(default, skip_serializing_if = "Vec::is_empty")]
131 pub any_of: Vec<ReferenceOr<Schema>>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub not: Option<Box<ReferenceOr<Schema>>>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
137#[serde(rename_all = "camelCase")]
138pub struct StringType {
139 #[serde(default, skip_serializing_if = "VariantOrUnknownOrEmpty::is_empty")]
140 pub format: VariantOrUnknownOrEmpty<StringFormat>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub pattern: Option<String>,
143 #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
144 pub enumeration: Vec<Option<String>>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub min_length: Option<usize>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub max_length: Option<usize>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
152#[serde(rename_all = "camelCase")]
153pub struct NumberType {
154 #[serde(default, skip_serializing_if = "VariantOrUnknownOrEmpty::is_empty")]
155 pub format: VariantOrUnknownOrEmpty<NumberFormat>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub multiple_of: Option<f64>,
158 #[serde(default, skip_serializing_if = "is_false")]
159 pub exclusive_minimum: bool,
160 #[serde(default, skip_serializing_if = "is_false")]
161 pub exclusive_maximum: bool,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub minimum: Option<f64>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub maximum: Option<f64>,
166 #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
167 pub enumeration: Vec<Option<f64>>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
171#[serde(rename_all = "camelCase")]
172pub struct IntegerType {
173 #[serde(default, skip_serializing_if = "VariantOrUnknownOrEmpty::is_empty")]
174 pub format: VariantOrUnknownOrEmpty<IntegerFormat>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub multiple_of: Option<i64>,
177 #[serde(default, skip_serializing_if = "is_false")]
178 pub exclusive_minimum: bool,
179 #[serde(default, skip_serializing_if = "is_false")]
180 pub exclusive_maximum: bool,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub minimum: Option<i64>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub maximum: Option<i64>,
185 #[serde(rename = "enum", default, skip_serializing_if = "Vec::is_empty")]
186 pub enumeration: Vec<Option<i64>>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
190#[serde(rename_all = "camelCase")]
191pub struct ObjectType {
192 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
193 pub properties: IndexMap<String, ReferenceOr<Box<Schema>>>,
194 #[serde(default, skip_serializing_if = "Vec::is_empty")]
195 pub required: Vec<String>,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub additional_properties: Option<AdditionalProperties>,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub min_properties: Option<usize>,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub max_properties: Option<usize>,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
205#[serde(rename_all = "camelCase")]
206pub struct ArrayType {
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub items: Option<ReferenceOr<Box<Schema>>>,
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub min_items: Option<usize>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub max_items: Option<usize>,
213 #[serde(default, skip_serializing_if = "is_false")]
214 pub unique_items: bool,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
218#[serde(rename_all = "lowercase")]
219pub enum NumberFormat {
220 Float,
221 Double,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
225#[serde(rename_all = "lowercase")]
226pub enum IntegerFormat {
227 Int32,
228 Int64,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
232#[serde(rename_all = "lowercase")]
233pub enum StringFormat {
234 Date,
235 #[serde(rename = "date-time")]
236 DateTime,
237 Password,
238 Byte,
239 Binary,
240}
241
242#[cfg(test)]
243mod tests {
244 use serde_json::json;
245
246 use crate::{AnySchema, Schema, SchemaData, SchemaKind};
247
248 #[test]
249 fn test_schema_with_extensions() {
250 let schema = serde_json::from_str::<Schema>(
251 r#"{
252 "type": "boolean",
253 "x-foo": "bar"
254 }"#,
255 )
256 .unwrap();
257
258 assert_eq!(
259 schema.schema_data.extensions.get("x-foo"),
260 Some(&json!("bar"))
261 );
262 }
263
264 #[test]
265 fn test_any() {
266 let value = json! { {} };
267 serde_json::from_value::<AnySchema>(value).unwrap();
268 }
269
270 #[test]
271 fn test_not() {
272 let value = json! {
273 {
274 "not": {}
275 }
276 };
277
278 let schema = serde_json::from_value::<Schema>(value).unwrap();
279 assert!(matches!(schema.schema_kind, SchemaKind::Not { not: _ }));
280 }
281
282 #[test]
283 fn test_null() {
284 let value = json! {
285 {
286 "nullable": true,
287 "enum": [ null ],
288 }
289 };
290
291 let schema = serde_json::from_value::<Schema>(value).unwrap();
292 assert!(matches!(
293 &schema.schema_data,
294 SchemaData { nullable: true, .. }
295 ));
296 assert!(matches!(
297 &schema.schema_kind,
298 SchemaKind::Any(AnySchema { enumeration, .. }) if enumeration[0] == json!(null)));
299 }
300}