1use serde::{Deserialize, Serialize};
2use std::fmt::Debug;
3
4use indexmap::IndexMap;
5use serde_json::Value;
6
7use crate::core::{DiffResult, Either, MayBeRefCore, ReferenceDescriptor};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct OpenApi310Ref {
11 #[serde(rename = "$ref")]
12 pub reference: String,
13}
14
15impl ReferenceDescriptor for OpenApi310Ref {
16 fn reference(&self) -> &str {
17 &self.reference
18 }
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct OpenApi310RefDiff {
23 #[serde(rename = "$ref")]
24 pub reference: DiffResult<String>,
25}
26
27impl ReferenceDescriptor for OpenApi310RefDiff {
28 fn reference(&self) -> &str {
29 self.reference.get().expect("Reference diff cannot be null")
30 }
31}
32
33pub type MayBeRef310<T> = MayBeRefCore<T, OpenApi310Ref>;
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct OpenApi310 {
38 pub openapi: String,
39 pub info: Option<Info>,
40 pub servers: Option<Vec<Server>>,
41 pub paths: Option<IndexMap<String, MayBeRef310<Path>>>,
42 pub components: Option<Components>,
43 pub tags: Option<Vec<Tag>>,
46 pub external_docs: Option<ExternalDoc>,
47}
48
49impl OpenApi310 {
50 pub const fn id() -> &'static str {
51 "OpenApi310"
52 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56#[serde(rename_all = "camelCase")]
57pub struct Info {
58 pub title: Option<String>,
59 pub description: Option<String>,
60 pub terms_of_service: Option<String>,
61
62 pub contact: Option<Contact>,
63 pub license: Option<License>,
64
65 pub version: Option<String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct Contact {
70 pub name: Option<String>,
71 pub url: Option<String>,
72 pub email: Option<String>,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct License {
77 pub name: Option<String>,
78 pub url: Option<String>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct Server {
83 pub url: Option<String>,
84 pub description: Option<String>,
85 pub variables: Option<IndexMap<String, ServerVariable>>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct ServerVariable {
90 pub r#enum: Option<Vec<String>>,
91 pub default: Option<Value>,
92 pub description: Option<String>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct Components {
98 pub schemas: Option<IndexMap<String, MayBeRef310<Schema>>>,
99 pub responses: Option<IndexMap<String, MayBeRef310<Response>>>,
100 pub parameters: Option<IndexMap<String, MayBeRef310<Parameter>>>,
101 pub examples: Option<IndexMap<String, MayBeRef310<Example>>>,
102 pub request_bodies: Option<IndexMap<String, MayBeRef310<RequestBody>>>,
103 pub headers: Option<IndexMap<String, MayBeRef310<Header>>>,
104 pub security_schemes:
105 Option<IndexMap<String, MayBeRef310<SecurityScheme>>>,
106 pub links: Option<IndexMap<String, MayBeRef310<Link>>>,
107 }
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ExternalDoc {
112 pub url: Option<String>,
113 pub description: Option<String>,
114}
115
116#[derive(Serialize, Deserialize, Debug, Clone, Default)]
117#[serde(rename_all = "camelCase")]
118pub struct Parameter {
119 pub name: String,
120 pub r#in: String,
121
122 pub description: Option<String>,
123
124 pub required: Option<bool>,
125 pub deprecated: Option<bool>,
126 pub allow_empty_value: Option<bool>,
127
128 pub style: Option<String>,
129 pub explode: Option<bool>,
130 pub allow_reserved: Option<bool>,
131
132 pub schema: Option<MayBeRef310<Schema>>,
133
134 pub example: Option<Value>,
135 pub examples: Option<IndexMap<String, MayBeRef310<Value>>>,
136
137 pub content: Option<IndexMap<String, MediaType>>,
138
139 #[serde(flatten)]
140 pub custom_fields: IndexMap<String, Value>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct RequestBody {
145 pub description: Option<String>,
146 pub content: Option<IndexMap<String, MediaType>>,
147 pub required: Option<bool>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct MediaType {
152 pub schema: Option<MayBeRef310<Schema>>,
153 pub example: Option<Value>,
154
155 pub examples: Option<IndexMap<String, MayBeRef310<Example>>>,
156 pub encoding: Option<IndexMap<String, Encoding>>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160#[serde(rename_all = "camelCase")]
161pub struct Encoding {
162 pub content_type: Option<String>,
163 pub headers: Option<IndexMap<String, MayBeRef310<Header>>>,
164 pub style: Option<String>,
165 pub explode: Option<bool>,
166 pub allow_reserved: Option<bool>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(rename_all = "camelCase")]
171pub struct Link {
172 pub operation_ref: Option<String>,
173 pub operation_id: Option<String>,
174 pub parameters: Option<IndexMap<String, Value>>,
175 pub request_body: Option<Value>,
176 pub description: Option<String>,
177 pub server: Option<Server>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct Response {
182 pub description: Option<String>,
183 pub headers: Option<IndexMap<String, MayBeRef310<Header>>>,
184 pub content: Option<IndexMap<String, MediaType>>,
185
186 pub links: Option<IndexMap<String, MayBeRef310<Link>>>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct Example {
192 pub summary: Option<String>,
193 pub description: Option<String>,
194 pub value: Option<Value>,
195 pub external_value: Option<String>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct Discriminator {
201 pub property_name: Option<String>,
202 pub mapping: Option<IndexMap<String, String>>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206#[serde(rename_all = "camelCase")]
207pub struct Xml {
208 pub name: Option<String>,
209 pub namespace: Option<String>,
210 pub prefix: Option<String>,
211 pub attribute: Option<bool>,
212 pub wrapped: Option<bool>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct SecurityScheme {
218 pub r#type: Option<String>,
219 pub description: Option<String>,
220 pub name: Option<String>,
221 pub r#in: Option<String>,
222 pub scheme: Option<String>,
223 pub bearer_format: Option<String>,
224 pub flows: Option<OAuthFlows>,
225 pub open_id_connect_url: Option<String>,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct OAuthFlows {
231 pub implicit: Option<OAuthFlow>,
232 pub password: Option<OAuthFlow>,
233 pub client_credentials: Option<OAuthFlow>,
234 pub authorization_code: Option<OAuthFlow>,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub struct OAuthFlow {
240 pub authorization_url: Option<String>,
241 pub token_url: Option<String>,
242 pub refresh_url: Option<String>,
243 pub scopes: Option<IndexMap<String, String>>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub struct Tag {
249 pub name: Option<String>,
250 pub description: Option<String>,
251 pub external_doc: Option<ExternalDoc>,
252}
253
254#[derive(Debug, Clone, Default, Serialize, Deserialize)]
255#[serde(rename_all = "camelCase")]
256pub struct Schema {
257 pub title: Option<String>,
286 pub multiple_of: Option<f32>,
287 pub maximum: Option<f32>,
288 pub exclusive_maximum: Option<bool>,
289 pub minimum: Option<f32>,
290 pub exclusive_minimum: Option<bool>,
291 pub max_length: Option<usize>,
292 pub min_length: Option<usize>,
293 pub pattern: Option<String>,
294 pub max_items: Option<usize>,
295 pub min_items: Option<usize>,
296 pub unique_items: Option<bool>,
297 pub max_properties: Option<usize>,
298 pub min_properties: Option<usize>,
299 pub required: Option<Vec<String>>,
300 pub r#enum: Option<Vec<Value>>,
301
302 pub r#type: Option<Either<String, Vec<String>>>,
315 pub all_of: Option<Vec<MayBeRef310<Schema>>>,
316 pub one_of: Option<Vec<MayBeRef310<Schema>>>,
317 pub any_of: Option<Vec<MayBeRef310<Schema>>>,
318 pub not: Option<Vec<MayBeRef310<Schema>>>,
319
320 pub items: Box<Option<MayBeRef310<Schema>>>,
321
322 pub properties: Option<IndexMap<String, MayBeRef310<Schema>>>,
326 pub additional_properties: Option<Either<bool, MayBeRef310<Schema>>>, pub description: Option<String>,
333 pub format: Option<String>,
334 pub default: Option<Value>,
335
336 pub discriminator: Option<Discriminator>,
337 pub read_only: Option<bool>,
338 pub write_only: Option<bool>,
339 pub xml: Option<Xml>,
340 pub external_docs: Option<ExternalDoc>,
341 pub example: Option<Value>,
342
343 pub deprecated: Option<bool>,
345
346 #[serde(flatten)]
347 pub custom_fields: IndexMap<String, Value>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
351#[serde(rename_all = "camelCase")]
352pub struct Header {
353 pub description: Option<String>,
354
355 pub required: Option<bool>,
356 pub deprecated: Option<bool>,
357 pub allow_empty_value: Option<bool>,
358
359 pub style: Option<String>,
360 pub explode: Option<bool>,
361 pub allow_reserved: Option<bool>,
362
363 pub schema: Option<MayBeRef310<Schema>>,
364
365 pub example: Option<Value>,
366 pub examples: Option<IndexMap<String, MayBeRef310<Value>>>,
367
368 pub content: Option<IndexMap<String, MediaType>>,
369
370 #[serde(flatten)]
371 pub custom_fields: IndexMap<String, Value>,
372}
373
374#[derive(Debug, Clone, Default, Serialize, Deserialize)]
375#[serde(rename_all = "camelCase")]
376pub struct Operation {
377 pub tags: Option<Vec<String>>,
378 pub summary: Option<String>,
379 pub description: Option<String>,
380
381 pub external_docs: Option<ExternalDoc>,
382
383 pub operation_id: Option<String>,
384
385 pub parameters: Option<Vec<MayBeRef310<Parameter>>>,
386
387 pub responses: Option<IndexMap<String, MayBeRef310<Response>>>,
388
389 pub request_body: Option<MayBeRef310<RequestBody>>,
390 pub servers: Option<Vec<Server>>,
391
392 pub security: Option<Vec<IndexMap<String, Vec<String>>>>,
393
394 pub deprecated: Option<bool>,
397}
398
399#[derive(Debug, Clone, Default, Serialize, Deserialize)]
400pub struct Path {
401 pub get: Option<Operation>,
402 pub put: Option<Operation>,
403 pub post: Option<Operation>,
404 pub delete: Option<Operation>,
405 pub options: Option<Operation>,
406 pub head: Option<Operation>,
407 pub patch: Option<Operation>,
408 pub trace: Option<Operation>,
409
410 pub servers: Option<Vec<Server>>,
411 pub parameters: Option<Vec<MayBeRef310<Parameter>>>,
412
413 pub summary: Option<String>,
414 pub description: Option<String>,
415}
416
417#[cfg(test)]
418mod tests {
419 use crate::schemas::openapi310::schema::*;
420 use indexmap::IndexMap;
421 use serde_json::json;
422
423 #[test]
424 fn check_request() {
425 let content = json!(
426 {"AuthentiqID":{"content":{"application/jwt":{"schema":{"$ref":"#/components/schemas/AuthentiqID"}}},"description":"Authentiq ID to register","required":true}}
427 );
428
429 let _: Option<IndexMap<String, MayBeRef310<RequestBody>>> =
430 serde_json::from_value(content).unwrap();
431 }
432
433 #[test]
434 fn check_operation() {
435 let op_def = r#"{
436 "post": {
437 "tags": ["Nodes"],
438 "summary": "Export Xlsx Template",
439 "description": "Generate XLSX-template for aggregated node data editing",
440 "operationId": "export_xlsx_template_api_v2_nodes__path__template_generate__post",
441 "parameters": [
442 {
443 "required": true,
444 "schema": { "title": "Path", "type": "string" },
445 "name": "path",
446 "in": "path"
447 },
448 {
449 "required": false,
450 "schema": { "title": "Update Sender", "type": "string" },
451 "name": "update_sender",
452 "in": "query"
453 },
454 {
455 "required": false,
456 "schema": { "title": "Force", "type": "boolean", "default": false },
457 "name": "force",
458 "in": "query"
459 },
460 {
461 "required": false,
462 "schema": { "title": "Compound Amount", "type": "integer" },
463 "name": "compound_amount",
464 "in": "query"
465 },
466 {
467 "required": false,
468 "schema": {
469 "allOf": [{ "$ref": "/components/schemas/ExportFmt" }],
470 "default": "xlsx"
471 },
472 "name": "export_format",
473 "in": "query"
474 }
475 ],
476 "requestBody": {
477 "content": {
478 "application/json": {
479 "schema": {
480 "$ref": "/components/schemas/Body_export_xlsx_template_api_v2_nodes__path__template_generate__post"
481 }
482 }
483 }
484 },
485 "responses": {
486 "200": {
487 "description": "Successful Response",
488 "content": {
489 "application/json": { "schema": {} },
490 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {}
491 }
492 },
493 "422": {
494 "description": "Validation Error",
495 "content": {
496 "application/json": {
497 "schema": { "$ref": "/components/schemas/HTTPValidationError" }
498 }
499 }
500 }
501 },
502 "security": [{ "OAuth2PasswordBearer": [] }]
503 }
504 }"#;
505
506 let _: Path = serde_json::from_str(op_def).unwrap();
507 }
508
509 #[test]
510 fn check_schem_additional_properties() {
511 let op_def = r#"{
512 "title": "AdditionalProperties",
513 "type": "object",
514 "additionalProperties": {
515 "$ref": "/components/schemas/AdditionalProperties"
516 }
517 }"#;
518
519 let op: Schema = serde_json::from_str(op_def).unwrap();
520 assert!(matches!(op.additional_properties, Some(Either::Right(_))));
521
522 let op_def = r#"{
523 "title": "AdditionalProperties",
524 "type": "object",
525 "additionalProperties": false
526 }"#;
527
528 let op: Schema = serde_json::from_str(op_def).unwrap();
529 assert!(matches!(op.additional_properties, Some(Either::Left(_))));
530
531 let sc_def = r#"
532 {
533 "type": "object",
534 "discriminator": { "propertyName": "type" },
535 "properties": {
536 "type": {
537 "type": "string",
538 "description": "The type of context being attached to the entity.",
539 "enum": ["link", "image"]
540 }
541 },
542 "required": ["type"]
543 }
544 "#;
545 let op: Schema = serde_json::from_str(sc_def).unwrap();
546 assert!(matches!(op.discriminator, Some(_)))
547 }
548}