1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct OpenApiSpec {
7 pub openapi: String,
9
10 pub info: ApiInfo,
12
13 #[serde(skip_serializing_if = "Vec::is_empty", default)]
15 pub servers: Vec<Server>,
16
17 #[serde(default)]
19 pub paths: HashMap<String, PathItem>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub components: Option<Components>,
24
25 #[serde(skip_serializing_if = "Vec::is_empty", default)]
27 pub security: Vec<SecurityRequirement>,
28
29 #[serde(skip_serializing_if = "Vec::is_empty", default)]
31 pub tags: Vec<Tag>,
32
33 #[serde(rename = "externalDocs", skip_serializing_if = "Option::is_none")]
35 pub external_docs: Option<ExternalDocumentation>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct ApiInfo {
41 pub title: String,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub description: Option<String>,
47
48 #[serde(rename = "termsOfService", skip_serializing_if = "Option::is_none")]
50 pub terms_of_service: Option<String>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub contact: Option<Contact>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub license: Option<License>,
59
60 pub version: String,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct Contact {
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub name: Option<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub url: Option<String>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub email: Option<String>,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct License {
78 pub name: String,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub url: Option<String>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct Server {
86 pub url: String,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub description: Option<String>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub variables: Option<HashMap<String, ServerVariable>>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ServerVariable {
101 pub default: String,
103
104 #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
106 pub enum_values: Option<Vec<String>>,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub description: Option<String>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, Default)]
115pub struct PathItem {
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub summary: Option<String>,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub description: Option<String>,
123
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub get: Option<Operation>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub put: Option<Operation>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub post: Option<Operation>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub delete: Option<Operation>,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub options: Option<Operation>,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub head: Option<Operation>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub patch: Option<Operation>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub trace: Option<Operation>,
155
156 #[serde(skip_serializing_if = "Vec::is_empty", default)]
158 pub parameters: Vec<Parameter>,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163pub struct Operation {
164 #[serde(skip_serializing_if = "Vec::is_empty", default)]
166 pub tags: Vec<String>,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub summary: Option<String>,
171
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub description: Option<String>,
175
176 #[serde(rename = "externalDocs", skip_serializing_if = "Option::is_none")]
178 pub external_docs: Option<ExternalDocumentation>,
179
180 #[serde(rename = "operationId", skip_serializing_if = "Option::is_none")]
182 pub operation_id: Option<String>,
183
184 #[serde(skip_serializing_if = "Vec::is_empty", default)]
186 pub parameters: Vec<Parameter>,
187
188 #[serde(rename = "requestBody", skip_serializing_if = "Option::is_none")]
190 pub request_body: Option<RequestBody>,
191
192 #[serde(default)]
194 pub responses: HashMap<String, Response>,
195
196 #[serde(skip_serializing_if = "Vec::is_empty", default)]
198 pub security: Vec<SecurityRequirement>,
199
200 #[serde(skip_serializing_if = "Vec::is_empty", default)]
202 pub servers: Vec<Server>,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub deprecated: Option<bool>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Parameter {
212 pub name: String,
214
215 #[serde(rename = "in")]
217 pub location: String,
218
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub description: Option<String>,
222
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub required: Option<bool>,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub deprecated: Option<bool>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub schema: Option<Schema>,
234
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub example: Option<serde_json::Value>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct RequestBody {
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub description: Option<String>,
246
247 pub content: HashMap<String, MediaType>,
249
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub required: Option<bool>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct Response {
258 pub description: String,
260
261 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
263 pub headers: HashMap<String, Header>,
264
265 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
267 pub content: HashMap<String, MediaType>,
268
269 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
271 pub links: HashMap<String, Link>,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct Header {
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub description: Option<String>,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub required: Option<bool>,
281 #[serde(skip_serializing_if = "Option::is_none")]
282 pub deprecated: Option<bool>,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub schema: Option<Schema>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct MediaType {
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub schema: Option<Schema>,
293
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub example: Option<serde_json::Value>,
297
298 #[serde(skip_serializing_if = "HashMap::is_empty")]
300 pub examples: HashMap<String, Example>,
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct Example {
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub summary: Option<String>,
308 #[serde(skip_serializing_if = "Option::is_none")]
309 pub description: Option<String>,
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub value: Option<serde_json::Value>,
312 #[serde(rename = "externalValue", skip_serializing_if = "Option::is_none")]
313 pub external_value: Option<String>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct Link {
319 #[serde(rename = "operationRef", skip_serializing_if = "Option::is_none")]
320 pub operation_ref: Option<String>,
321 #[serde(rename = "operationId", skip_serializing_if = "Option::is_none")]
322 pub operation_id: Option<String>,
323 #[serde(skip_serializing_if = "HashMap::is_empty")]
324 pub parameters: HashMap<String, serde_json::Value>,
325 #[serde(rename = "requestBody", skip_serializing_if = "Option::is_none")]
326 pub request_body: Option<serde_json::Value>,
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub description: Option<String>,
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize, Default)]
333pub struct Schema {
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub title: Option<String>,
337
338 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
340 pub schema_type: Option<String>,
341
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub format: Option<String>,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub description: Option<String>,
349
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub default: Option<serde_json::Value>,
353
354 #[serde(skip_serializing_if = "Option::is_none")]
356 pub example: Option<serde_json::Value>,
357
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub nullable: Option<bool>,
361
362 #[serde(skip_serializing_if = "HashMap::is_empty")]
364 pub properties: HashMap<String, Schema>,
365
366 #[serde(skip_serializing_if = "Vec::is_empty")]
368 pub required: Vec<String>,
369
370 #[serde(
372 rename = "additionalProperties",
373 skip_serializing_if = "Option::is_none"
374 )]
375 pub additional_properties: Option<Box<Schema>>,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub items: Option<Box<Schema>>,
380
381 #[serde(rename = "enum", skip_serializing_if = "Vec::is_empty")]
383 pub enum_values: Vec<serde_json::Value>,
384
385 #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
387 pub reference: Option<String>,
388
389 #[serde(rename = "allOf", skip_serializing_if = "Vec::is_empty")]
391 pub all_of: Vec<Schema>,
392
393 #[serde(rename = "anyOf", skip_serializing_if = "Vec::is_empty")]
395 pub any_of: Vec<Schema>,
396
397 #[serde(rename = "oneOf", skip_serializing_if = "Vec::is_empty")]
399 pub one_of: Vec<Schema>,
400
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub minimum: Option<f64>,
404
405 #[serde(skip_serializing_if = "Option::is_none")]
407 pub maximum: Option<f64>,
408
409 #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
411 pub min_length: Option<usize>,
412
413 #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
415 pub max_length: Option<usize>,
416
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub pattern: Option<String>,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, Default)]
424pub struct Components {
425 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
427 pub schemas: HashMap<String, Schema>,
428
429 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
431 pub responses: HashMap<String, Response>,
432
433 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
435 pub parameters: HashMap<String, Parameter>,
436
437 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
439 pub examples: HashMap<String, Example>,
440
441 #[serde(
443 rename = "requestBodies",
444 skip_serializing_if = "HashMap::is_empty",
445 default
446 )]
447 pub request_bodies: HashMap<String, RequestBody>,
448
449 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
451 pub headers: HashMap<String, Header>,
452
453 #[serde(
455 rename = "securitySchemes",
456 skip_serializing_if = "HashMap::is_empty",
457 default
458 )]
459 pub security_schemes: HashMap<String, SecurityScheme>,
460
461 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
463 pub links: HashMap<String, Link>,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468#[serde(tag = "type")]
469pub enum SecurityScheme {
470 #[serde(rename = "apiKey")]
471 ApiKey {
472 name: String,
473 #[serde(rename = "in")]
474 location: String,
475 },
476 #[serde(rename = "http")]
477 Http {
478 scheme: String,
479 #[serde(rename = "bearerFormat", skip_serializing_if = "Option::is_none")]
480 bearer_format: Option<String>,
481 },
482 #[serde(rename = "oauth2")]
483 OAuth2 { flows: OAuth2Flows },
484 #[serde(rename = "openIdConnect")]
485 OpenIdConnect {
486 #[serde(rename = "openIdConnectUrl")]
487 open_id_connect_url: String,
488 },
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct OAuth2Flows {
494 #[serde(rename = "implicit", skip_serializing_if = "Option::is_none")]
495 pub implicit: Option<OAuth2Flow>,
496 #[serde(rename = "password", skip_serializing_if = "Option::is_none")]
497 pub password: Option<OAuth2Flow>,
498 #[serde(rename = "clientCredentials", skip_serializing_if = "Option::is_none")]
499 pub client_credentials: Option<OAuth2Flow>,
500 #[serde(rename = "authorizationCode", skip_serializing_if = "Option::is_none")]
501 pub authorization_code: Option<OAuth2Flow>,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize)]
506pub struct OAuth2Flow {
507 #[serde(rename = "authorizationUrl", skip_serializing_if = "Option::is_none")]
508 pub authorization_url: Option<String>,
509 #[serde(rename = "tokenUrl", skip_serializing_if = "Option::is_none")]
510 pub token_url: Option<String>,
511 #[serde(rename = "refreshUrl", skip_serializing_if = "Option::is_none")]
512 pub refresh_url: Option<String>,
513 pub scopes: HashMap<String, String>,
514}
515
516pub type SecurityRequirement = HashMap<String, Vec<String>>;
518
519#[derive(Debug, Clone, Serialize, Deserialize)]
521pub struct Tag {
522 pub name: String,
523 #[serde(skip_serializing_if = "Option::is_none")]
524 pub description: Option<String>,
525 #[serde(rename = "externalDocs", skip_serializing_if = "Option::is_none")]
526 pub external_docs: Option<ExternalDocumentation>,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct ExternalDocumentation {
532 pub url: String,
533 #[serde(skip_serializing_if = "Option::is_none")]
534 pub description: Option<String>,
535}
536
537impl OpenApiSpec {
538 pub fn new(title: &str, version: &str) -> Self {
540 Self {
541 openapi: "3.0.3".to_string(),
542 info: ApiInfo {
543 title: title.to_string(),
544 description: None,
545 terms_of_service: None,
546 contact: None,
547 license: None,
548 version: version.to_string(),
549 },
550 servers: Vec::new(),
551 paths: HashMap::new(),
552 components: None,
553 security: Vec::new(),
554 tags: Vec::new(),
555 external_docs: None,
556 }
557 }
558}