1#![warn(missing_docs)]
8
9use serde::{Deserialize, Serialize};
10
11pub mod openapi;
13pub mod swagger_ui;
15use serde_json;
16
17#[cfg(feature = "yaml")]
18use serde_yaml;
19
20pub trait ToSchema {
24 fn schema() -> Schema;
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
30#[serde(rename_all = "lowercase")]
31pub enum SchemaType {
32 String,
34 Integer,
36 Number,
38 Boolean,
40 Array,
42 Object,
44 Null,
46}
47
48impl Default for SchemaType {
49 fn default() -> Self {
51 Self::Object
52 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct Schema {
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub title: Option<String>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub description: Option<String>,
64 #[serde(rename = "type")]
66 pub schema_type: SchemaType,
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub properties: Option<std::collections::BTreeMap<String, Schema>>,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub required: Option<Vec<String>>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub items: Option<Box<Schema>>,
76 #[serde(rename = "enum")]
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub enum_values: Option<Vec<serde_json::Value>>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub default: Option<serde_json::Value>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub example: Option<serde_json::Value>,
86 #[serde(default)]
88 pub nullable: bool,
89 #[serde(default)]
91 pub read_only: bool,
92 #[serde(default)]
94 pub write_only: bool,
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub minimum: Option<f64>,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub maximum: Option<f64>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub min_length: Option<usize>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub max_length: Option<usize>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub pattern: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub format: Option<String>,
113 #[serde(rename = "oneOf")]
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub one_of: Option<Vec<Schema>>,
117 #[serde(rename = "anyOf")]
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub any_of: Option<Vec<Schema>>,
121 #[serde(rename = "allOf")]
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub all_of: Option<Vec<Schema>>,
125}
126
127impl Default for Schema {
128 fn default() -> Self {
130 Self {
131 title: None,
132 description: None,
133 schema_type: SchemaType::Object,
134 properties: None,
135 required: None,
136 items: None,
137 enum_values: None,
138 default: None,
139 example: None,
140 nullable: false,
141 read_only: false,
142 write_only: false,
143 minimum: None,
144 maximum: None,
145 min_length: None,
146 max_length: None,
147 pattern: None,
148 format: None,
149 one_of: None,
150 any_of: None,
151 all_of: None,
152 }
153 }
154}
155
156impl Schema {
157 pub fn string() -> Self {
159 Self { schema_type: SchemaType::String, ..Default::default() }
160 }
161
162 pub fn integer() -> Self {
164 Self { schema_type: SchemaType::Integer, ..Default::default() }
165 }
166
167 pub fn number() -> Self {
169 Self { schema_type: SchemaType::Number, ..Default::default() }
170 }
171
172 pub fn boolean() -> Self {
174 Self { schema_type: SchemaType::Boolean, ..Default::default() }
175 }
176
177 pub fn array(items: Schema) -> Self {
179 Self { schema_type: SchemaType::Array, items: Some(Box::new(items)), ..Default::default() }
180 }
181
182 pub fn object() -> Self {
184 Self { schema_type: SchemaType::Object, properties: Some(std::collections::BTreeMap::new()), ..Default::default() }
185 }
186
187 pub fn title(mut self, title: impl Into<String>) -> Self {
189 self.title = Some(title.into());
190 self
191 }
192
193 pub fn description(mut self, desc: impl Into<String>) -> Self {
195 self.description = Some(desc.into());
196 self
197 }
198
199 pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
201 let properties = self.properties.get_or_insert_with(std::collections::BTreeMap::new);
202 properties.insert(name.into(), schema);
203 self
204 }
205
206 pub fn required(mut self, fields: Vec<&str>) -> Self {
208 self.required = Some(fields.into_iter().map(String::from).collect());
209 self
210 }
211
212 pub fn enum_values(mut self, values: Vec<serde_json::Value>) -> Self {
214 self.enum_values = Some(values);
215 self
216 }
217
218 pub fn with_default(mut self, value: serde_json::Value) -> Self {
220 self.default = Some(value);
221 self
222 }
223
224 pub fn example(mut self, value: serde_json::Value) -> Self {
226 self.example = Some(value);
227 self
228 }
229
230 pub fn format(mut self, format: impl Into<String>) -> Self {
232 self.format = Some(format.into());
233 self
234 }
235
236 pub fn min_length(mut self, len: usize) -> Self {
238 self.min_length = Some(len);
239 self
240 }
241
242 pub fn max_length(mut self, len: usize) -> Self {
244 self.max_length = Some(len);
245 self
246 }
247
248 pub fn minimum(mut self, min: f64) -> Self {
250 self.minimum = Some(min);
251 self
252 }
253
254 pub fn maximum(mut self, max: f64) -> Self {
256 self.maximum = Some(max);
257 self
258 }
259
260 pub fn nullable(mut self, nullable: bool) -> Self {
262 self.nullable = nullable;
263 self
264 }
265
266 pub fn read_only(mut self, read_only: bool) -> Self {
268 self.read_only = read_only;
269 self
270 }
271
272 pub fn write_only(mut self, write_only: bool) -> Self {
274 self.write_only = write_only;
275 self
276 }
277
278 pub fn one_of(mut self, schemas: Vec<Schema>) -> Self {
280 self.one_of = Some(schemas);
281 self
282 }
283
284 pub fn any_of(mut self, schemas: Vec<Schema>) -> Self {
286 self.any_of = Some(schemas);
287 self
288 }
289
290 pub fn all_of(mut self, schemas: Vec<Schema>) -> Self {
292 self.all_of = Some(schemas);
293 self
294 }
295
296 pub fn to_json_schema(&self) -> serde_json::Value {
298 serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
299 }
300}
301
302pub struct SchemaBuilder {
304 schema: Schema,
305}
306
307impl SchemaBuilder {
308 pub fn new() -> Self {
310 Self { schema: <Schema as Default>::default() }
311 }
312
313 pub fn string() -> Self {
315 Self { schema: Schema::string() }
316 }
317
318 pub fn integer() -> Self {
320 Self { schema: Schema::integer() }
321 }
322
323 pub fn number() -> Self {
325 Self { schema: Schema::number() }
326 }
327
328 pub fn boolean() -> Self {
330 Self { schema: Schema::boolean() }
331 }
332
333 pub fn array(items: Schema) -> Self {
335 Self { schema: Schema::array(items) }
336 }
337
338 pub fn object() -> Self {
340 Self { schema: Schema::object() }
341 }
342
343 pub fn title(mut self, title: impl Into<String>) -> Self {
345 self.schema.title = Some(title.into());
346 self
347 }
348
349 pub fn description(mut self, desc: impl Into<String>) -> Self {
351 self.schema.description = Some(desc.into());
352 self
353 }
354
355 pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
357 let properties = self.schema.properties.get_or_insert_with(std::collections::BTreeMap::new);
358 properties.insert(name.into(), schema);
359 self
360 }
361
362 pub fn required(mut self, fields: Vec<&str>) -> Self {
364 self.schema.required = Some(fields.into_iter().map(String::from).collect());
365 self
366 }
367
368 pub fn build(self) -> Schema {
370 self.schema
371 }
372}
373
374impl Default for SchemaBuilder {
375 fn default() -> Self {
377 Self::new()
378 }
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct OpenApiInfo {
384 pub title: String,
386 pub version: String,
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub description: Option<String>,
391 #[serde(skip_serializing_if = "Option::is_none")]
393 pub terms_of_service: Option<String>,
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub contact: Option<Contact>,
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub license: Option<License>,
400}
401
402impl Default for OpenApiInfo {
403 fn default() -> Self {
405 Self {
406 title: "API".to_string(),
407 version: "1.0.0".to_string(),
408 description: None,
409 terms_of_service: None,
410 contact: None,
411 license: None,
412 }
413 }
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct Contact {
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub name: Option<String>,
422 #[serde(skip_serializing_if = "Option::is_none")]
424 pub email: Option<String>,
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub url: Option<String>,
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct License {
433 pub name: String,
435 #[serde(skip_serializing_if = "Option::is_none")]
437 pub url: Option<String>,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct ExternalDocumentation {
443 pub url: String,
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub description: Option<String>,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct Tag {
453 pub name: String,
455 #[serde(skip_serializing_if = "Option::is_none")]
457 pub description: Option<String>,
458 #[serde(skip_serializing_if = "Option::is_none")]
460 pub external_docs: Option<ExternalDocumentation>,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct OpenApiDoc {
466 pub openapi: String,
468 pub info: OpenApiInfo,
470 pub paths: std::collections::BTreeMap<String, PathItem>,
472 #[serde(skip_serializing_if = "Option::is_none")]
474 pub components: Option<Components>,
475 #[serde(skip_serializing_if = "Option::is_none")]
477 pub servers: Option<Vec<Server>>,
478 #[serde(skip_serializing_if = "Option::is_none")]
480 pub tags: Option<Vec<Tag>>,
481 #[serde(skip_serializing_if = "Option::is_none")]
483 pub external_docs: Option<ExternalDocumentation>,
484 #[serde(skip_serializing_if = "Option::is_none")]
486 pub security: Option<Vec<SecurityRequirement>>,
487}
488
489impl Default for OpenApiDoc {
490 fn default() -> Self {
492 Self {
493 openapi: "3.1.0".to_string(),
494 info: OpenApiInfo::default(),
495 paths: std::collections::BTreeMap::new(),
496 components: None,
497 servers: None,
498 tags: None,
499 external_docs: None,
500 security: None,
501 }
502 }
503}
504
505impl OpenApiDoc {
506 pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
508 Self { info: OpenApiInfo { title: title.into(), version: version.into(), ..Default::default() }, ..Default::default() }
509 }
510
511 pub fn description(mut self, desc: impl Into<String>) -> Self {
513 self.info.description = Some(desc.into());
514 self
515 }
516
517 pub fn path(mut self, path: impl Into<String>, item: PathItem) -> Self {
519 self.paths.insert(path.into(), item);
520 self
521 }
522
523 pub fn schema(mut self, name: impl Into<String>, schema: Schema) -> Self {
525 let components = self.components.get_or_insert_with(Components::default);
526 components.schemas.insert(name.into(), schema);
527 self
528 }
529
530 pub fn security_scheme(mut self, name: impl Into<String>, scheme: SecurityScheme) -> Self {
532 let components = self.components.get_or_insert_with(Components::default);
533 components.security_schemes.insert(name.into(), scheme);
534 self
535 }
536
537 pub fn response(mut self, name: impl Into<String>, response: Response) -> Self {
539 let components = self.components.get_or_insert_with(Components::default);
540 components.responses.insert(name.into(), response);
541 self
542 }
543
544 pub fn parameter(mut self, name: impl Into<String>, parameter: Parameter) -> Self {
546 let components = self.components.get_or_insert_with(Components::default);
547 components.parameters.insert(name.into(), parameter);
548 self
549 }
550
551 pub fn request_body(mut self, name: impl Into<String>, body: RequestBody) -> Self {
553 let components = self.components.get_or_insert_with(Components::default);
554 components.request_bodies.insert(name.into(), body);
555 self
556 }
557
558 pub fn server(mut self, url: impl Into<String>, description: Option<String>) -> Self {
560 let servers = self.servers.get_or_insert_with(Vec::new);
561 servers.push(Server { url: url.into(), description, variables: None });
562 self
563 }
564
565 pub fn tag(mut self, name: impl Into<String>, description: Option<String>) -> Self {
567 let tags = self.tags.get_or_insert_with(Vec::new);
568 tags.push(Tag { name: name.into(), description, external_docs: None });
569 self
570 }
571
572 pub fn external_docs(mut self, url: impl Into<String>, description: Option<String>) -> Self {
574 self.external_docs = Some(ExternalDocumentation { url: url.into(), description });
575 self
576 }
577
578 pub fn security(mut self, security: SecurityRequirement) -> Self {
580 let securities = self.security.get_or_insert_with(Vec::new);
581 securities.push(security);
582 self
583 }
584
585 pub fn to_json(&self) -> serde_json::Value {
587 serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
588 }
589
590 #[cfg(feature = "yaml")]
592 pub fn to_yaml(&self) -> String {
593 serde_yaml::to_string(self).unwrap_or_default()
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct PathItem {
600 #[serde(skip_serializing_if = "Option::is_none")]
602 pub get: Option<Operation>,
603 #[serde(skip_serializing_if = "Option::is_none")]
605 pub post: Option<Operation>,
606 #[serde(skip_serializing_if = "Option::is_none")]
608 pub put: Option<Operation>,
609 #[serde(skip_serializing_if = "Option::is_none")]
611 pub delete: Option<Operation>,
612 #[serde(skip_serializing_if = "Option::is_none")]
614 pub patch: Option<Operation>,
615 #[serde(skip_serializing_if = "Option::is_none")]
617 pub head: Option<Operation>,
618 #[serde(skip_serializing_if = "Option::is_none")]
620 pub options: Option<Operation>,
621 #[serde(skip_serializing_if = "Option::is_none")]
623 pub trace: Option<Operation>,
624 #[serde(skip_serializing_if = "Option::is_none")]
626 pub description: Option<String>,
627 #[serde(skip_serializing_if = "Option::is_none")]
629 pub summary: Option<String>,
630 #[serde(skip_serializing_if = "Option::is_none")]
632 pub parameters: Option<Vec<ParameterOrReference>>,
633 #[serde(skip_serializing_if = "Option::is_none")]
635 pub servers: Option<Vec<Server>>,
636}
637
638impl Default for PathItem {
639 fn default() -> Self {
641 Self {
642 get: None,
643 post: None,
644 put: None,
645 delete: None,
646 patch: None,
647 head: None,
648 options: None,
649 trace: None,
650 description: None,
651 summary: None,
652 parameters: None,
653 servers: None,
654 }
655 }
656}
657
658impl PathItem {
659 pub fn new() -> Self {
661 Self::default()
662 }
663
664 pub fn get(mut self, op: Operation) -> Self {
666 self.get = Some(op);
667 self
668 }
669
670 pub fn post(mut self, op: Operation) -> Self {
672 self.post = Some(op);
673 self
674 }
675
676 pub fn put(mut self, op: Operation) -> Self {
678 self.put = Some(op);
679 self
680 }
681
682 pub fn delete(mut self, op: Operation) -> Self {
684 self.delete = Some(op);
685 self
686 }
687
688 pub fn patch(mut self, op: Operation) -> Self {
690 self.patch = Some(op);
691 self
692 }
693
694 pub fn head(mut self, op: Operation) -> Self {
696 self.head = Some(op);
697 self
698 }
699
700 pub fn options(mut self, op: Operation) -> Self {
702 self.options = Some(op);
703 self
704 }
705
706 pub fn trace(mut self, op: Operation) -> Self {
708 self.trace = Some(op);
709 self
710 }
711}
712
713#[derive(Debug, Clone, Serialize, Deserialize)]
715pub struct Operation {
716 #[serde(skip_serializing_if = "Option::is_none")]
718 pub tags: Option<Vec<String>>,
719 #[serde(skip_serializing_if = "Option::is_none")]
721 pub summary: Option<String>,
722 #[serde(skip_serializing_if = "Option::is_none")]
724 pub description: Option<String>,
725 #[serde(skip_serializing_if = "Option::is_none")]
727 pub operation_id: Option<String>,
728 #[serde(skip_serializing_if = "Option::is_none")]
730 pub parameters: Option<Vec<ParameterOrReference>>,
731 #[serde(skip_serializing_if = "Option::is_none")]
733 pub request_body: Option<RequestBodyOrReference>,
734 pub responses: std::collections::BTreeMap<String, ResponseOrReference>,
736 #[serde(skip_serializing_if = "Option::is_none")]
738 pub callbacks: Option<std::collections::BTreeMap<String, Callback>>,
739 #[serde(default)]
741 pub deprecated: bool,
742 #[serde(skip_serializing_if = "Option::is_none")]
744 pub security: Option<Vec<SecurityRequirement>>,
745 #[serde(skip_serializing_if = "Option::is_none")]
747 pub servers: Option<Vec<Server>>,
748 #[serde(skip_serializing_if = "Option::is_none")]
750 pub external_docs: Option<ExternalDocumentation>,
751}
752
753impl Operation {
754 pub fn new() -> Self {
756 Self {
757 tags: None,
758 summary: None,
759 description: None,
760 operation_id: None,
761 parameters: None,
762 request_body: None,
763 responses: std::collections::BTreeMap::new(),
764 callbacks: None,
765 deprecated: false,
766 security: None,
767 servers: None,
768 external_docs: None,
769 }
770 }
771
772 pub fn summary(mut self, summary: impl Into<String>) -> Self {
774 self.summary = Some(summary.into());
775 self
776 }
777
778 pub fn description(mut self, desc: impl Into<String>) -> Self {
780 self.description = Some(desc.into());
781 self
782 }
783
784 pub fn operation_id(mut self, id: impl Into<String>) -> Self {
786 self.operation_id = Some(id.into());
787 self
788 }
789
790 pub fn tag(mut self, tag: impl Into<String>) -> Self {
792 let tags = self.tags.get_or_insert_with(Vec::new);
793 tags.push(tag.into());
794 self
795 }
796
797 pub fn parameter(mut self, param: ParameterOrReference) -> Self {
799 let params = self.parameters.get_or_insert_with(Vec::new);
800 params.push(param);
801 self
802 }
803
804 pub fn request_body(mut self, body: RequestBodyOrReference) -> Self {
806 self.request_body = Some(body);
807 self
808 }
809
810 pub fn response(mut self, status: impl Into<String>, resp: ResponseOrReference) -> Self {
812 self.responses.insert(status.into(), resp);
813 self
814 }
815
816 pub fn deprecated(mut self, deprecated: bool) -> Self {
818 self.deprecated = deprecated;
819 self
820 }
821}
822
823impl Default for Operation {
824 fn default() -> Self {
826 Self::new()
827 }
828}
829
830#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct Parameter {
833 pub name: String,
835 #[serde(rename = "in")]
837 pub location: ParameterLocation,
838 #[serde(skip_serializing_if = "Option::is_none")]
840 pub description: Option<String>,
841 #[serde(default)]
843 pub required: bool,
844 #[serde(skip_serializing_if = "Option::is_none")]
846 pub schema: Option<Schema>,
847 #[serde(default)]
849 pub deprecated: bool,
850 #[serde(default)]
852 pub allow_empty_value: bool,
853 #[serde(skip_serializing_if = "Option::is_none")]
855 pub example: Option<serde_json::Value>,
856 #[serde(skip_serializing_if = "Option::is_none")]
858 pub examples: Option<std::collections::BTreeMap<String, ExampleOrReference>>,
859}
860
861#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
863#[serde(rename_all = "lowercase")]
864pub enum ParameterLocation {
865 Path,
867 Query,
869 Header,
871 Cookie,
873}
874
875impl Parameter {
876 pub fn path(name: impl Into<String>) -> Self {
878 Self {
879 name: name.into(),
880 location: ParameterLocation::Path,
881 description: None,
882 required: true,
883 schema: None,
884 deprecated: false,
885 allow_empty_value: false,
886 example: None,
887 examples: None,
888 }
889 }
890
891 pub fn query(name: impl Into<String>) -> Self {
893 Self {
894 name: name.into(),
895 location: ParameterLocation::Query,
896 description: None,
897 required: false,
898 schema: None,
899 deprecated: false,
900 allow_empty_value: false,
901 example: None,
902 examples: None,
903 }
904 }
905
906 pub fn header(name: impl Into<String>) -> Self {
908 Self {
909 name: name.into(),
910 location: ParameterLocation::Header,
911 description: None,
912 required: false,
913 schema: None,
914 deprecated: false,
915 allow_empty_value: false,
916 example: None,
917 examples: None,
918 }
919 }
920
921 pub fn cookie(name: impl Into<String>) -> Self {
923 Self {
924 name: name.into(),
925 location: ParameterLocation::Cookie,
926 description: None,
927 required: false,
928 schema: None,
929 deprecated: false,
930 allow_empty_value: false,
931 example: None,
932 examples: None,
933 }
934 }
935
936 pub fn description(mut self, desc: impl Into<String>) -> Self {
938 self.description = Some(desc.into());
939 self
940 }
941
942 pub fn required(mut self, required: bool) -> Self {
944 self.required = required;
945 self
946 }
947
948 pub fn schema(mut self, schema: Schema) -> Self {
950 self.schema = Some(schema);
951 self
952 }
953
954 pub fn deprecated(mut self, deprecated: bool) -> Self {
956 self.deprecated = deprecated;
957 self
958 }
959
960 pub fn allow_empty_value(mut self, allow: bool) -> Self {
962 self.allow_empty_value = allow;
963 self
964 }
965
966 pub fn example(mut self, example: serde_json::Value) -> Self {
968 self.example = Some(example);
969 self
970 }
971}
972
973#[derive(Debug, Clone, Serialize, Deserialize)]
975pub struct Reference {
976 #[serde(rename = "$ref")]
978 pub ref_path: String,
979}
980
981#[derive(Debug, Clone, Serialize, Deserialize)]
983#[serde(untagged)]
984pub enum ParameterOrReference {
985 Parameter(Parameter),
987 Reference(Reference),
989}
990
991#[derive(Debug, Clone, Serialize, Deserialize)]
993pub struct RequestBody {
994 #[serde(skip_serializing_if = "Option::is_none")]
996 pub description: Option<String>,
997 pub content: std::collections::BTreeMap<String, MediaType>,
999 #[serde(default)]
1001 pub required: bool,
1002}
1003
1004impl RequestBody {
1005 pub fn json(schema: Schema) -> Self {
1007 let mut content = std::collections::BTreeMap::new();
1008 content.insert(
1009 "application/json".to_string(),
1010 MediaType { schema: Some(schema), example: None, examples: None, encoding: None },
1011 );
1012 Self { description: None, content, required: true }
1013 }
1014
1015 pub fn description(mut self, desc: impl Into<String>) -> Self {
1017 self.description = Some(desc.into());
1018 self
1019 }
1020
1021 pub fn required(mut self, required: bool) -> Self {
1023 self.required = required;
1024 self
1025 }
1026}
1027
1028#[derive(Debug, Clone, Serialize, Deserialize)]
1030#[serde(untagged)]
1031pub enum RequestBodyOrReference {
1032 RequestBody(RequestBody),
1034 Reference(Reference),
1036}
1037
1038#[derive(Debug, Clone, Serialize, Deserialize)]
1040pub struct MediaType {
1041 #[serde(skip_serializing_if = "Option::is_none")]
1043 pub schema: Option<Schema>,
1044 #[serde(skip_serializing_if = "Option::is_none")]
1046 pub example: Option<serde_json::Value>,
1047 #[serde(skip_serializing_if = "Option::is_none")]
1049 pub examples: Option<std::collections::BTreeMap<String, ExampleOrReference>>,
1050 #[serde(skip_serializing_if = "Option::is_none")]
1052 pub encoding: Option<std::collections::BTreeMap<String, Encoding>>,
1053}
1054
1055#[derive(Debug, Clone, Serialize, Deserialize)]
1057pub struct Encoding {
1058 #[serde(skip_serializing_if = "Option::is_none")]
1060 pub content_type: Option<String>,
1061 #[serde(skip_serializing_if = "Option::is_none")]
1063 pub headers: Option<std::collections::BTreeMap<String, HeaderOrReference>>,
1064 #[serde(skip_serializing_if = "Option::is_none")]
1066 pub style: Option<String>,
1067 #[serde(default)]
1069 pub explode: bool,
1070 #[serde(default)]
1072 pub allow_reserved: bool,
1073}
1074
1075#[derive(Debug, Clone, Serialize, Deserialize)]
1077pub struct Response {
1078 pub description: String,
1080 #[serde(skip_serializing_if = "Option::is_none")]
1082 pub content: Option<std::collections::BTreeMap<String, MediaType>>,
1083 #[serde(skip_serializing_if = "Option::is_none")]
1085 pub headers: Option<std::collections::BTreeMap<String, HeaderOrReference>>,
1086 #[serde(skip_serializing_if = "Option::is_none")]
1088 pub links: Option<std::collections::BTreeMap<String, LinkOrReference>>,
1089}
1090
1091impl Response {
1092 pub fn new(description: impl Into<String>) -> Self {
1094 Self { description: description.into(), content: None, headers: None, links: None }
1095 }
1096
1097 pub fn json(mut self, schema: Schema) -> Self {
1099 let content = self.content.get_or_insert_with(std::collections::BTreeMap::new);
1100 content.insert(
1101 "application/json".to_string(),
1102 MediaType { schema: Some(schema), example: None, examples: None, encoding: None },
1103 );
1104 self
1105 }
1106}
1107
1108#[derive(Debug, Clone, Serialize, Deserialize)]
1110#[serde(untagged)]
1111pub enum ResponseOrReference {
1112 Response(Response),
1114 Reference(Reference),
1116}
1117
1118#[derive(Debug, Clone, Serialize, Deserialize)]
1120pub struct Header {
1121 #[serde(skip_serializing_if = "Option::is_none")]
1123 pub description: Option<String>,
1124 #[serde(default)]
1126 pub required: bool,
1127 #[serde(skip_serializing_if = "Option::is_none")]
1129 pub schema: Option<Schema>,
1130 #[serde(default)]
1132 pub deprecated: bool,
1133 #[serde(skip_serializing_if = "Option::is_none")]
1135 pub example: Option<serde_json::Value>,
1136 #[serde(skip_serializing_if = "Option::is_none")]
1138 pub examples: Option<std::collections::BTreeMap<String, ExampleOrReference>>,
1139}
1140
1141#[derive(Debug, Clone, Serialize, Deserialize)]
1143#[serde(untagged)]
1144pub enum HeaderOrReference {
1145 Header(Header),
1147 Reference(Reference),
1149}
1150
1151#[derive(Debug, Clone, Serialize, Deserialize)]
1153pub struct Example {
1154 #[serde(skip_serializing_if = "Option::is_none")]
1156 pub summary: Option<String>,
1157 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub description: Option<String>,
1160 #[serde(skip_serializing_if = "Option::is_none")]
1162 pub value: Option<serde_json::Value>,
1163 #[serde(skip_serializing_if = "Option::is_none")]
1165 pub external_value: Option<String>,
1166}
1167
1168#[derive(Debug, Clone, Serialize, Deserialize)]
1170#[serde(untagged)]
1171pub enum ExampleOrReference {
1172 Example(Example),
1174 Reference(Reference),
1176}
1177
1178#[derive(Debug, Clone, Serialize, Deserialize)]
1180pub struct Link {
1181 #[serde(skip_serializing_if = "Option::is_none")]
1183 pub operation_ref: Option<String>,
1184 #[serde(skip_serializing_if = "Option::is_none")]
1186 pub operation_id: Option<String>,
1187 #[serde(skip_serializing_if = "Option::is_none")]
1189 pub parameters: Option<std::collections::BTreeMap<String, serde_json::Value>>,
1190 #[serde(skip_serializing_if = "Option::is_none")]
1192 pub request_body: Option<serde_json::Value>,
1193 #[serde(skip_serializing_if = "Option::is_none")]
1195 pub description: Option<String>,
1196 #[serde(skip_serializing_if = "Option::is_none")]
1198 pub server: Option<Server>,
1199}
1200
1201#[derive(Debug, Clone, Serialize, Deserialize)]
1203#[serde(untagged)]
1204pub enum LinkOrReference {
1205 Link(Link),
1207 Reference(Reference),
1209}
1210
1211#[derive(Debug, Clone, Serialize, Deserialize)]
1213pub struct Callback {
1214 #[serde(flatten)]
1216 pub paths: std::collections::BTreeMap<String, PathItem>,
1217}
1218
1219#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1221pub struct SecurityRequirement {
1222 #[serde(flatten)]
1224 pub schemes: std::collections::BTreeMap<String, Vec<String>>,
1225}
1226
1227impl SecurityRequirement {
1228 pub fn new() -> Self {
1230 Self::default()
1231 }
1232
1233 pub fn scheme(mut self, name: impl Into<String>, scopes: Vec<&str>) -> Self {
1235 self.schemes.insert(name.into(), scopes.into_iter().map(String::from).collect());
1236 self
1237 }
1238}
1239
1240#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1242#[serde(rename_all = "lowercase")]
1243pub enum SecuritySchemeType {
1244 ApiKey,
1246 Http,
1248 OAuth2,
1250 OpenIdConnect,
1252}
1253
1254#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1256#[serde(rename_all = "lowercase")]
1257pub enum ApiKeyLocation {
1258 Query,
1260 Header,
1262 Cookie,
1264}
1265
1266#[derive(Debug, Clone, Serialize, Deserialize)]
1268pub struct SecurityScheme {
1269 #[serde(rename = "type")]
1271 pub scheme_type: SecuritySchemeType,
1272 #[serde(skip_serializing_if = "Option::is_none")]
1274 pub description: Option<String>,
1275 #[serde(skip_serializing_if = "Option::is_none")]
1277 pub name: Option<String>,
1278 #[serde(rename = "in")]
1280 #[serde(skip_serializing_if = "Option::is_none")]
1281 pub location: Option<ApiKeyLocation>,
1282 #[serde(skip_serializing_if = "Option::is_none")]
1284 pub scheme: Option<String>,
1285 #[serde(skip_serializing_if = "Option::is_none")]
1287 pub bearer_format: Option<String>,
1288 #[serde(skip_serializing_if = "Option::is_none")]
1290 pub flows: Option<OAuthFlows>,
1291 #[serde(skip_serializing_if = "Option::is_none")]
1293 pub open_id_connect_url: Option<String>,
1294}
1295
1296impl SecurityScheme {
1297 pub fn api_key(name: impl Into<String>, location: ApiKeyLocation) -> Self {
1299 Self {
1300 scheme_type: SecuritySchemeType::ApiKey,
1301 description: None,
1302 name: Some(name.into()),
1303 location: Some(location),
1304 scheme: None,
1305 bearer_format: None,
1306 flows: None,
1307 open_id_connect_url: None,
1308 }
1309 }
1310
1311 pub fn basic() -> Self {
1313 Self {
1314 scheme_type: SecuritySchemeType::Http,
1315 description: None,
1316 name: None,
1317 location: None,
1318 scheme: Some("basic".to_string()),
1319 bearer_format: None,
1320 flows: None,
1321 open_id_connect_url: None,
1322 }
1323 }
1324
1325 pub fn bearer(format: Option<String>) -> Self {
1327 Self {
1328 scheme_type: SecuritySchemeType::Http,
1329 description: None,
1330 name: None,
1331 location: None,
1332 scheme: Some("bearer".to_string()),
1333 bearer_format: format,
1334 flows: None,
1335 open_id_connect_url: None,
1336 }
1337 }
1338
1339 pub fn oauth2(flows: OAuthFlows) -> Self {
1341 Self {
1342 scheme_type: SecuritySchemeType::OAuth2,
1343 description: None,
1344 name: None,
1345 location: None,
1346 scheme: None,
1347 bearer_format: None,
1348 flows: Some(flows),
1349 open_id_connect_url: None,
1350 }
1351 }
1352
1353 pub fn open_id_connect(url: impl Into<String>) -> Self {
1355 Self {
1356 scheme_type: SecuritySchemeType::OpenIdConnect,
1357 description: None,
1358 name: None,
1359 location: None,
1360 scheme: None,
1361 bearer_format: None,
1362 flows: None,
1363 open_id_connect_url: Some(url.into()),
1364 }
1365 }
1366
1367 pub fn description(mut self, desc: impl Into<String>) -> Self {
1369 self.description = Some(desc.into());
1370 self
1371 }
1372}
1373
1374#[derive(Debug, Clone, Serialize, Deserialize)]
1376pub struct OAuthFlows {
1377 #[serde(skip_serializing_if = "Option::is_none")]
1379 pub implicit: Option<OAuthFlow>,
1380 #[serde(skip_serializing_if = "Option::is_none")]
1382 pub password: Option<OAuthFlow>,
1383 #[serde(skip_serializing_if = "Option::is_none")]
1385 pub client_credentials: Option<OAuthFlow>,
1386 #[serde(skip_serializing_if = "Option::is_none")]
1388 pub authorization_code: Option<OAuthFlow>,
1389}
1390
1391#[derive(Debug, Clone, Serialize, Deserialize)]
1393pub struct OAuthFlow {
1394 #[serde(skip_serializing_if = "Option::is_none")]
1396 pub authorization_url: Option<String>,
1397 #[serde(skip_serializing_if = "Option::is_none")]
1399 pub token_url: Option<String>,
1400 #[serde(skip_serializing_if = "Option::is_none")]
1402 pub refresh_url: Option<String>,
1403 pub scopes: std::collections::BTreeMap<String, String>,
1405}
1406
1407impl OAuthFlow {
1408 pub fn new() -> Self {
1410 Self { authorization_url: None, token_url: None, refresh_url: None, scopes: std::collections::BTreeMap::new() }
1411 }
1412
1413 pub fn authorization_url(mut self, url: impl Into<String>) -> Self {
1415 self.authorization_url = Some(url.into());
1416 self
1417 }
1418
1419 pub fn token_url(mut self, url: impl Into<String>) -> Self {
1421 self.token_url = Some(url.into());
1422 self
1423 }
1424
1425 pub fn refresh_url(mut self, url: impl Into<String>) -> Self {
1427 self.refresh_url = Some(url.into());
1428 self
1429 }
1430
1431 pub fn scope(mut self, name: impl Into<String>, description: impl Into<String>) -> Self {
1433 self.scopes.insert(name.into(), description.into());
1434 self
1435 }
1436}
1437
1438impl Default for OAuthFlow {
1439 fn default() -> Self {
1440 Self::new()
1441 }
1442}
1443
1444#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1446pub struct Components {
1447 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1449 pub schemas: std::collections::BTreeMap<String, Schema>,
1450 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1452 pub responses: std::collections::BTreeMap<String, Response>,
1453 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1455 pub parameters: std::collections::BTreeMap<String, Parameter>,
1456 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1458 pub examples: std::collections::BTreeMap<String, Example>,
1459 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1461 pub request_bodies: std::collections::BTreeMap<String, RequestBody>,
1462 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1464 pub headers: std::collections::BTreeMap<String, Header>,
1465 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1467 pub security_schemes: std::collections::BTreeMap<String, SecurityScheme>,
1468 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1470 pub links: std::collections::BTreeMap<String, Link>,
1471 #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
1473 pub callbacks: std::collections::BTreeMap<String, Callback>,
1474}
1475
1476#[derive(Debug, Clone, Serialize, Deserialize)]
1478pub struct Server {
1479 pub url: String,
1481 #[serde(skip_serializing_if = "Option::is_none")]
1483 pub description: Option<String>,
1484 #[serde(skip_serializing_if = "Option::is_none")]
1486 pub variables: Option<std::collections::BTreeMap<String, ServerVariable>>,
1487}
1488
1489#[derive(Debug, Clone, Serialize, Deserialize)]
1491pub struct ServerVariable {
1492 pub default: String,
1494 #[serde(skip_serializing_if = "Option::is_none")]
1496 pub enum_values: Option<Vec<String>>,
1497 #[serde(skip_serializing_if = "Option::is_none")]
1499 pub description: Option<String>,
1500}
1501
1502impl ToSchema for String {
1503 fn schema() -> Schema {
1504 Schema::string()
1505 }
1506}
1507
1508impl ToSchema for i64 {
1509 fn schema() -> Schema {
1510 Schema::integer()
1511 }
1512}
1513
1514impl ToSchema for i32 {
1515 fn schema() -> Schema {
1516 Schema::integer()
1517 }
1518}
1519
1520impl ToSchema for i16 {
1521 fn schema() -> Schema {
1522 Schema::integer()
1523 }
1524}
1525
1526impl ToSchema for i8 {
1527 fn schema() -> Schema {
1528 Schema::integer()
1529 }
1530}
1531
1532impl ToSchema for u64 {
1533 fn schema() -> Schema {
1534 Schema::integer()
1535 }
1536}
1537
1538impl ToSchema for u32 {
1539 fn schema() -> Schema {
1540 Schema::integer()
1541 }
1542}
1543
1544impl ToSchema for u16 {
1545 fn schema() -> Schema {
1546 Schema::integer()
1547 }
1548}
1549
1550impl ToSchema for u8 {
1551 fn schema() -> Schema {
1552 Schema::integer()
1553 }
1554}
1555
1556impl ToSchema for f64 {
1557 fn schema() -> Schema {
1558 Schema::number()
1559 }
1560}
1561
1562impl ToSchema for f32 {
1563 fn schema() -> Schema {
1564 Schema::number()
1565 }
1566}
1567
1568impl ToSchema for bool {
1569 fn schema() -> Schema {
1570 Schema::boolean()
1571 }
1572}
1573
1574impl<T: ToSchema> ToSchema for Vec<T> {
1575 fn schema() -> Schema {
1576 Schema::array(T::schema())
1577 }
1578}
1579
1580impl<T: ToSchema> ToSchema for Option<T> {
1581 fn schema() -> Schema {
1582 T::schema().nullable(true)
1583 }
1584}
1585
1586impl ToSchema for serde_json::Value {
1587 fn schema() -> Schema {
1588 <Schema as Default>::default()
1589 }
1590}