1use crate::Path;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::BTreeMap;
8
9use super::{
10 builder,
11 content::Content,
12 extensions::Extensions,
13 request_body::RequestBody,
14 response::{Response, Responses},
15 security::SecurityRequirement,
16 set_value, Deprecated, ExternalDocs, Ref, RefOr, Required, Schema, Server,
17};
18
19#[cfg(not(feature = "preserve_path_order"))]
20#[allow(missing_docs)]
21#[doc(hidden)]
22pub type PathsMap<K, V> = std::collections::BTreeMap<K, V>;
23#[cfg(feature = "preserve_path_order")]
24#[allow(missing_docs)]
25#[doc(hidden)]
26pub type PathsMap<K, V> = indexmap::IndexMap<K, V>;
27
28pub type Callback = BTreeMap<String, RefOr<PathItem>>;
30
31builder! {
32 PathsBuilder;
33
34 #[non_exhaustive]
41 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
42 #[cfg_attr(feature = "debug", derive(Debug))]
43 pub struct Paths {
44 #[serde(flatten)]
47 pub paths: PathsMap<String, PathItem>,
48
49 #[serde(skip_serializing_if = "Option::is_none", flatten)]
51 pub extensions: Option<Extensions>,
52 }
53}
54
55impl Paths {
56 pub fn new() -> Self {
58 Default::default()
59 }
60
61 pub fn get_path_item<P: AsRef<str>>(&self, path: P) -> Option<&PathItem> {
73 self.paths.get(path.as_ref())
74 }
75
76 pub fn get_path_operation<P: AsRef<str>>(
90 &self,
91 path: P,
92 http_method: HttpMethod,
93 ) -> Option<&Operation> {
94 self.paths
95 .get(path.as_ref())
96 .and_then(|path| match http_method {
97 HttpMethod::Get => path.get.as_ref(),
98 HttpMethod::Put => path.put.as_ref(),
99 HttpMethod::Post => path.post.as_ref(),
100 HttpMethod::Delete => path.delete.as_ref(),
101 HttpMethod::Options => path.options.as_ref(),
102 HttpMethod::Head => path.head.as_ref(),
103 HttpMethod::Patch => path.patch.as_ref(),
104 HttpMethod::Trace => path.trace.as_ref(),
105 })
106 }
107
108 pub fn add_path_operation<P: AsRef<str>, O: Into<Operation>>(
116 &mut self,
117 path: P,
118 http_methods: Vec<HttpMethod>,
119 operation: O,
120 ) {
121 let path = path.as_ref();
122 let operation = operation.into();
123 if let Some(existing_item) = self.paths.get_mut(path) {
124 for http_method in http_methods {
125 match http_method {
126 HttpMethod::Get => existing_item.get = Some(operation.clone()),
127 HttpMethod::Put => existing_item.put = Some(operation.clone()),
128 HttpMethod::Post => existing_item.post = Some(operation.clone()),
129 HttpMethod::Delete => existing_item.delete = Some(operation.clone()),
130 HttpMethod::Options => existing_item.options = Some(operation.clone()),
131 HttpMethod::Head => existing_item.head = Some(operation.clone()),
132 HttpMethod::Patch => existing_item.patch = Some(operation.clone()),
133 HttpMethod::Trace => existing_item.trace = Some(operation.clone()),
134 };
135 }
136 } else {
137 self.paths.insert(
138 String::from(path),
139 PathItem::from_http_methods(http_methods, operation),
140 );
141 }
142 }
143
144 pub fn merge(&mut self, other_paths: Paths) {
148 for (path, that) in other_paths.paths {
149 if let Some(this) = self.paths.get_mut(&path) {
150 this.merge_operations(that);
151 } else {
152 self.paths.insert(path, that);
153 }
154 }
155
156 if let Some(other_paths_extensions) = other_paths.extensions {
157 let paths_extensions = self.extensions.get_or_insert(Extensions::default());
158 paths_extensions.merge(other_paths_extensions);
159 }
160 }
161}
162
163impl PathsBuilder {
164 pub fn path<I: Into<String>>(mut self, path: I, item: PathItem) -> Self {
167 let path_string = path.into();
168 if let Some(existing_item) = self.paths.get_mut(&path_string) {
169 existing_item.merge_operations(item);
170 } else {
171 self.paths.insert(path_string, item);
172 }
173
174 self
175 }
176
177 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
179 set_value!(self extensions extensions)
180 }
181
182 pub fn path_from<P: Path>(self) -> Self {
201 let methods = P::methods();
202 let operation = P::operation();
203
204 let path_item = if methods.len() == 1 {
206 PathItem::new(
207 methods
208 .into_iter()
209 .next()
210 .expect("must have one operation method"),
211 operation,
212 )
213 } else {
214 methods
215 .into_iter()
216 .fold(PathItemBuilder::new(), |path_item, method| {
217 path_item.operation(method, operation.clone())
218 })
219 .build()
220 };
221
222 self.path(P::path(), path_item)
223 }
224}
225
226builder! {
227 PathItemBuilder;
228
229 #[non_exhaustive]
234 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
235 #[cfg_attr(feature = "debug", derive(Debug))]
236 #[serde(rename_all = "camelCase")]
237 pub struct PathItem {
238 #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
240 pub ref_path: Option<String>,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub summary: Option<String>,
245
246 #[serde(skip_serializing_if = "Option::is_none")]
249 pub description: Option<String>,
250
251 #[serde(skip_serializing_if = "Option::is_none")]
254 pub servers: Option<Vec<Server>>,
255
256 #[serde(skip_serializing_if = "Option::is_none")]
260 pub parameters: Option<Vec<RefOr<Parameter>>>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub get: Option<Operation>,
265
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub put: Option<Operation>,
269
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub post: Option<Operation>,
273
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub delete: Option<Operation>,
277
278 #[serde(skip_serializing_if = "Option::is_none")]
280 pub options: Option<Operation>,
281
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub head: Option<Operation>,
285
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub patch: Option<Operation>,
289
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub trace: Option<Operation>,
293
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub query: Option<Operation>,
297
298 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
300 pub additional_operations: BTreeMap<String, Operation>,
301
302 #[serde(skip_serializing_if = "Option::is_none", flatten)]
304 pub extensions: Option<Extensions>,
305 }
306}
307
308impl PathItem {
309 pub fn new<O: Into<Operation>>(http_method: HttpMethod, operation: O) -> Self {
311 let mut path_item = Self::default();
312 match http_method {
313 HttpMethod::Get => path_item.get = Some(operation.into()),
314 HttpMethod::Put => path_item.put = Some(operation.into()),
315 HttpMethod::Post => path_item.post = Some(operation.into()),
316 HttpMethod::Delete => path_item.delete = Some(operation.into()),
317 HttpMethod::Options => path_item.options = Some(operation.into()),
318 HttpMethod::Head => path_item.head = Some(operation.into()),
319 HttpMethod::Patch => path_item.patch = Some(operation.into()),
320 HttpMethod::Trace => path_item.trace = Some(operation.into()),
321 };
322
323 path_item
324 }
325
326 pub fn from_http_methods<I: IntoIterator<Item = HttpMethod>, O: Into<Operation>>(
328 http_methods: I,
329 operation: O,
330 ) -> Self {
331 let mut path_item = Self::default();
332 let operation = operation.into();
333 for method in http_methods {
334 match method {
335 HttpMethod::Get => path_item.get = Some(operation.clone()),
336 HttpMethod::Put => path_item.put = Some(operation.clone()),
337 HttpMethod::Post => path_item.post = Some(operation.clone()),
338 HttpMethod::Delete => path_item.delete = Some(operation.clone()),
339 HttpMethod::Options => path_item.options = Some(operation.clone()),
340 HttpMethod::Head => path_item.head = Some(operation.clone()),
341 HttpMethod::Patch => path_item.patch = Some(operation.clone()),
342 HttpMethod::Trace => path_item.trace = Some(operation.clone()),
343 };
344 }
345
346 path_item
347 }
348
349 pub fn merge_operations(&mut self, path_item: PathItem) {
352 if path_item.get.is_some() && self.get.is_none() {
353 self.get = path_item.get;
354 }
355 if path_item.put.is_some() && self.put.is_none() {
356 self.put = path_item.put;
357 }
358 if path_item.post.is_some() && self.post.is_none() {
359 self.post = path_item.post;
360 }
361 if path_item.delete.is_some() && self.delete.is_none() {
362 self.delete = path_item.delete;
363 }
364 if path_item.options.is_some() && self.options.is_none() {
365 self.options = path_item.options;
366 }
367 if path_item.head.is_some() && self.head.is_none() {
368 self.head = path_item.head;
369 }
370 if path_item.patch.is_some() && self.patch.is_none() {
371 self.patch = path_item.patch;
372 }
373 if path_item.trace.is_some() && self.trace.is_none() {
374 self.trace = path_item.trace;
375 }
376 if path_item.query.is_some() && self.query.is_none() {
377 self.query = path_item.query;
378 }
379 for (method, operation) in path_item.additional_operations {
380 self.additional_operations
381 .entry(method)
382 .or_insert(operation);
383 }
384 }
385}
386
387impl PathItemBuilder {
388 pub fn ref_path<S: Into<String>>(mut self, ref_path: Option<S>) -> Self {
390 set_value!(self ref_path ref_path.map(Into::into))
391 }
392
393 pub fn operation<O: Into<Operation>>(mut self, http_method: HttpMethod, operation: O) -> Self {
396 match http_method {
397 HttpMethod::Get => self.get = Some(operation.into()),
398 HttpMethod::Put => self.put = Some(operation.into()),
399 HttpMethod::Post => self.post = Some(operation.into()),
400 HttpMethod::Delete => self.delete = Some(operation.into()),
401 HttpMethod::Options => self.options = Some(operation.into()),
402 HttpMethod::Head => self.head = Some(operation.into()),
403 HttpMethod::Patch => self.patch = Some(operation.into()),
404 HttpMethod::Trace => self.trace = Some(operation.into()),
405 };
406
407 self
408 }
409
410 pub fn query<O: Into<Operation>>(mut self, operation: Option<O>) -> Self {
412 set_value!(self query operation.map(Into::into))
413 }
414
415 pub fn additional_operations<
418 I: IntoIterator<Item = (S, O)>,
419 S: Into<String>,
420 O: Into<Operation>,
421 >(
422 mut self,
423 additional_operations: I,
424 ) -> Self {
425 self.additional_operations.extend(
426 additional_operations
427 .into_iter()
428 .map(|(method, operation)| (method.into(), operation.into())),
429 );
430
431 self
432 }
433
434 pub fn additional_operation<S: Into<String>, O: Into<Operation>>(
436 mut self,
437 method: S,
438 operation: O,
439 ) -> Self {
440 self.additional_operations
441 .insert(method.into(), operation.into());
442
443 self
444 }
445
446 pub fn summary<S: Into<String>>(mut self, summary: Option<S>) -> Self {
448 set_value!(self summary summary.map(|summary| summary.into()))
449 }
450
451 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
454 set_value!(self description description.map(|description| description.into()))
455 }
456
457 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: Option<I>) -> Self {
460 set_value!(self servers servers.map(|servers| servers.into_iter().collect()))
461 }
462
463 pub fn parameters<I: IntoIterator<Item = Parameter>>(mut self, parameters: Option<I>) -> Self {
465 set_value!(self parameters parameters.map(|parameters| parameters.into_iter().map(Into::into).collect()))
466 }
467
468 pub fn parameter_refs<I: IntoIterator<Item = Ref>>(mut self, refs: Option<I>) -> Self {
470 set_value!(self parameters refs.map(|refs| refs.into_iter().map(RefOr::Ref).collect()))
471 }
472
473 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
475 set_value!(self extensions extensions)
476 }
477}
478
479#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
483#[serde(rename_all = "lowercase")]
484#[cfg_attr(feature = "debug", derive(Debug))]
485pub enum HttpMethod {
486 Get,
488 Post,
490 Put,
492 Delete,
494 Options,
496 Head,
498 Patch,
500 Trace,
502}
503
504builder! {
505 OperationBuilder;
506
507 #[non_exhaustive]
511 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
512 #[cfg_attr(feature = "debug", derive(Debug))]
513 #[serde(rename_all = "camelCase")]
514 pub struct Operation {
515 #[serde(skip_serializing_if = "Option::is_none")]
525 pub tags: Option<Vec<String>>,
526
527 #[serde(skip_serializing_if = "Option::is_none")]
534 pub summary: Option<String>,
535
536 #[serde(skip_serializing_if = "Option::is_none")]
543 pub description: Option<String>,
544
545 #[serde(skip_serializing_if = "Option::is_none")]
552 pub operation_id: Option<String>,
553
554 #[serde(skip_serializing_if = "Option::is_none")]
556 pub external_docs: Option<ExternalDocs>,
557
558 #[serde(skip_serializing_if = "Option::is_none")]
560 pub parameters: Option<Vec<RefOr<Parameter>>>,
561
562 #[serde(skip_serializing_if = "Option::is_none")]
564 pub request_body: Option<RefOr<RequestBody>>,
565
566 pub responses: Responses,
568
569 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
571 pub callbacks: BTreeMap<String, RefOr<Callback>>,
572
573 #[serde(skip_serializing_if = "Option::is_none")]
575 pub deprecated: Option<Deprecated>,
576
577 #[serde(skip_serializing_if = "Option::is_none")]
583 pub security: Option<Vec<SecurityRequirement>>,
584
585 #[serde(skip_serializing_if = "Option::is_none")]
587 pub servers: Option<Vec<Server>>,
588
589 #[serde(skip_serializing_if = "Option::is_none", flatten)]
591 pub extensions: Option<Extensions>,
592 }
593}
594
595impl Operation {
596 pub fn new() -> Self {
598 Default::default()
599 }
600}
601
602impl OperationBuilder {
603 pub fn tags<I: IntoIterator<Item = V>, V: Into<String>>(mut self, tags: Option<I>) -> Self {
605 set_value!(self tags tags.map(|tags| tags.into_iter().map(Into::into).collect()))
606 }
607
608 pub fn tag<S: Into<String>>(mut self, tag: S) -> Self {
610 let tag_string = tag.into();
611 match self.tags {
612 Some(ref mut tags) => tags.push(tag_string),
613 None => {
614 self.tags = Some(vec![tag_string]);
615 }
616 }
617
618 self
619 }
620
621 pub fn summary<S: Into<String>>(mut self, summary: Option<S>) -> Self {
623 set_value!(self summary summary.map(|summary| summary.into()))
624 }
625
626 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
628 set_value!(self description description.map(|description| description.into()))
629 }
630
631 pub fn operation_id<S: Into<String>>(mut self, operation_id: Option<S>) -> Self {
633 set_value!(self operation_id operation_id.map(|operation_id| operation_id.into()))
634 }
635
636 pub fn parameters<I: IntoIterator<Item = P>, P: Into<Parameter>>(
638 mut self,
639 parameters: Option<I>,
640 ) -> Self {
641 self.parameters = parameters.map(|parameters| {
642 if let Some(mut params) = self.parameters {
643 params.extend(
644 parameters
645 .into_iter()
646 .map(|parameter| RefOr::T(parameter.into())),
647 );
648 params
649 } else {
650 parameters
651 .into_iter()
652 .map(|parameter| RefOr::T(parameter.into()))
653 .collect()
654 }
655 });
656
657 self
658 }
659
660 pub fn parameter<P: Into<Parameter>>(mut self, parameter: P) -> Self {
662 match self.parameters {
663 Some(ref mut parameters) => parameters.push(RefOr::T(parameter.into())),
664 None => {
665 self.parameters = Some(vec![RefOr::T(parameter.into())]);
666 }
667 }
668
669 self
670 }
671
672 pub fn parameter_ref(mut self, parameter: Ref) -> Self {
674 match self.parameters {
675 Some(ref mut parameters) => parameters.push(RefOr::Ref(parameter)),
676 None => {
677 self.parameters = Some(vec![RefOr::Ref(parameter)]);
678 }
679 }
680
681 self
682 }
683
684 pub fn request_body(mut self, request_body: Option<RequestBody>) -> Self {
686 set_value!(self request_body request_body.map(Into::into))
687 }
688
689 pub fn request_body_ref(mut self, request_body: Option<Ref>) -> Self {
691 set_value!(self request_body request_body.map(RefOr::Ref))
692 }
693
694 pub fn responses<R: Into<Responses>>(mut self, responses: R) -> Self {
696 set_value!(self responses responses.into())
697 }
698
699 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
704 mut self,
705 code: S,
706 response: R,
707 ) -> Self {
708 self.responses
709 .responses
710 .insert(code.into(), response.into());
711
712 self
713 }
714
715 pub fn callback<S: Into<String>, C: Into<RefOr<Callback>>>(
717 mut self,
718 name: S,
719 callback: C,
720 ) -> Self {
721 self.callbacks.insert(name.into(), callback.into());
722 self
723 }
724
725 pub fn callbacks_from_iter<
727 I: IntoIterator<Item = (S, C)>,
728 S: Into<String>,
729 C: Into<RefOr<Callback>>,
730 >(
731 mut self,
732 callbacks: I,
733 ) -> Self {
734 self.callbacks.extend(
735 callbacks
736 .into_iter()
737 .map(|(name, callback)| (name.into(), callback.into())),
738 );
739 self
740 }
741
742 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
744 set_value!(self deprecated deprecated)
745 }
746
747 pub fn securities<I: IntoIterator<Item = SecurityRequirement>>(
749 mut self,
750 securities: Option<I>,
751 ) -> Self {
752 set_value!(self security securities.map(|securities| securities.into_iter().collect()))
753 }
754
755 pub fn security(mut self, security: SecurityRequirement) -> Self {
757 if let Some(ref mut securities) = self.security {
758 securities.push(security);
759 } else {
760 self.security = Some(vec![security]);
761 }
762
763 self
764 }
765
766 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: Option<I>) -> Self {
768 set_value!(self servers servers.map(|servers| servers.into_iter().collect()))
769 }
770
771 pub fn server(mut self, server: Server) -> Self {
773 if let Some(ref mut servers) = self.servers {
774 servers.push(server);
775 } else {
776 self.servers = Some(vec![server]);
777 }
778
779 self
780 }
781
782 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
784 set_value!(self extensions extensions)
785 }
786}
787
788impl From<Ref> for RefOr<PathItem> {
789 fn from(r: Ref) -> Self {
790 Self::Ref(r)
791 }
792}
793
794impl From<Ref> for RefOr<Callback> {
795 fn from(r: Ref) -> Self {
796 Self::Ref(r)
797 }
798}
799
800builder! {
801 ParameterBuilder;
802
803 #[non_exhaustive]
807 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
808 #[cfg_attr(feature = "debug", derive(Debug))]
809 #[serde(rename_all = "camelCase")]
810 pub struct Parameter {
811 pub name: String,
816
817 #[serde(rename = "in")]
819 pub parameter_in: ParameterIn,
820
821 #[serde(skip_serializing_if = "Option::is_none")]
823 pub description: Option<String>,
824
825 pub required: Required,
829
830 #[serde(skip_serializing_if = "Option::is_none")]
832 pub deprecated: Option<Deprecated>,
833 #[serde(skip_serializing_if = "Option::is_none")]
836 pub schema: Option<RefOr<Schema>>,
837
838 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
840 pub content: BTreeMap<String, RefOr<Content>>,
841
842 #[serde(skip_serializing_if = "Option::is_none")]
845 pub style: Option<ParameterStyle>,
846
847 #[serde(skip_serializing_if = "Option::is_none")]
860 pub explode: Option<bool>,
861
862 #[serde(skip_serializing_if = "Option::is_none")]
866 pub allow_reserved: Option<bool>,
867
868 #[serde(skip_serializing_if = "Option::is_none")]
871 example: Option<Value>,
872
873 #[serde(skip_serializing_if = "Option::is_none", flatten)]
875 pub extensions: Option<Extensions>,
876 }
877}
878
879impl Parameter {
880 pub fn new<S: Into<String>>(name: S) -> Self {
882 Self {
883 name: name.into(),
884 required: Required::True,
885 ..Default::default()
886 }
887 }
888}
889
890impl ParameterBuilder {
891 pub fn name<I: Into<String>>(mut self, name: I) -> Self {
893 set_value!(self name name.into())
894 }
895
896 pub fn parameter_in(mut self, parameter_in: ParameterIn) -> Self {
898 set_value!(self parameter_in parameter_in)
899 }
900
901 pub fn required(mut self, required: Required) -> Self {
904 self.required = required;
905 if self.parameter_in == ParameterIn::Path {
907 self.required = Required::True;
908 }
909
910 self
911 }
912
913 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
915 set_value!(self description description.map(|description| description.into()))
916 }
917
918 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
920 set_value!(self deprecated deprecated)
921 }
922
923 pub fn schema<I: Into<RefOr<Schema>>>(mut self, component: Option<I>) -> Self {
925 set_value!(self schema component.map(|component| component.into()))
926 }
927
928 pub fn content<S: Into<String>, C: Into<RefOr<Content>>>(
930 mut self,
931 content_type: S,
932 content: C,
933 ) -> Self {
934 self.content.insert(content_type.into(), content.into());
935 self
936 }
937
938 pub fn contents_from_iter<
940 I: IntoIterator<Item = (S, C)>,
941 S: Into<String>,
942 C: Into<RefOr<Content>>,
943 >(
944 mut self,
945 content: I,
946 ) -> Self {
947 self.content.extend(
948 content
949 .into_iter()
950 .map(|(content_type, content)| (content_type.into(), content.into())),
951 );
952 self
953 }
954
955 pub fn style(mut self, style: Option<ParameterStyle>) -> Self {
957 set_value!(self style style)
958 }
959
960 pub fn explode(mut self, explode: Option<bool>) -> Self {
962 set_value!(self explode explode)
963 }
964
965 pub fn allow_reserved(mut self, allow_reserved: Option<bool>) -> Self {
967 set_value!(self allow_reserved allow_reserved)
968 }
969
970 pub fn example(mut self, example: Option<Value>) -> Self {
972 set_value!(self example example)
973 }
974
975 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
977 set_value!(self extensions extensions)
978 }
979}
980
981#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
983#[serde(rename_all = "lowercase")]
984#[cfg_attr(feature = "debug", derive(Debug))]
985pub enum ParameterIn {
986 Query,
988 Path,
990 Header,
992 Cookie,
994 #[serde(rename = "querystring")]
996 QueryString,
997}
998
999impl Default for ParameterIn {
1000 fn default() -> Self {
1001 Self::Path
1002 }
1003}
1004
1005#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1007#[cfg_attr(feature = "debug", derive(Debug))]
1008#[serde(rename_all = "camelCase")]
1009pub enum ParameterStyle {
1010 Matrix,
1014 Label,
1018 Form,
1022 Simple,
1025 SpaceDelimited,
1028 PipeDelimited,
1031 DeepObject,
1034 Cookie,
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040 use super::{HttpMethod, Operation, OperationBuilder};
1041 use crate::openapi::{security::SecurityRequirement, server::Server, PathItem, PathsBuilder};
1042
1043 #[test]
1044 fn test_path_order() {
1045 let paths_list = PathsBuilder::new()
1046 .path(
1047 "/todo",
1048 PathItem::new(HttpMethod::Get, OperationBuilder::new()),
1049 )
1050 .path(
1051 "/todo",
1052 PathItem::new(HttpMethod::Post, OperationBuilder::new()),
1053 )
1054 .path(
1055 "/todo/{id}",
1056 PathItem::new(HttpMethod::Delete, OperationBuilder::new()),
1057 )
1058 .path(
1059 "/todo/{id}",
1060 PathItem::new(HttpMethod::Get, OperationBuilder::new()),
1061 )
1062 .path(
1063 "/todo/{id}",
1064 PathItem::new(HttpMethod::Put, OperationBuilder::new()),
1065 )
1066 .path(
1067 "/todo/search",
1068 PathItem::new(HttpMethod::Get, OperationBuilder::new()),
1069 )
1070 .build();
1071
1072 let actual_value = paths_list
1073 .paths
1074 .iter()
1075 .flat_map(|(path, path_item)| {
1076 let mut path_methods =
1077 Vec::<(&str, &HttpMethod)>::with_capacity(paths_list.paths.len());
1078 if path_item.get.is_some() {
1079 path_methods.push((path, &HttpMethod::Get));
1080 }
1081 if path_item.put.is_some() {
1082 path_methods.push((path, &HttpMethod::Put));
1083 }
1084 if path_item.post.is_some() {
1085 path_methods.push((path, &HttpMethod::Post));
1086 }
1087 if path_item.delete.is_some() {
1088 path_methods.push((path, &HttpMethod::Delete));
1089 }
1090 if path_item.options.is_some() {
1091 path_methods.push((path, &HttpMethod::Options));
1092 }
1093 if path_item.head.is_some() {
1094 path_methods.push((path, &HttpMethod::Head));
1095 }
1096 if path_item.patch.is_some() {
1097 path_methods.push((path, &HttpMethod::Patch));
1098 }
1099 if path_item.trace.is_some() {
1100 path_methods.push((path, &HttpMethod::Trace));
1101 }
1102
1103 path_methods
1104 })
1105 .collect::<Vec<_>>();
1106
1107 let get = HttpMethod::Get;
1108 let post = HttpMethod::Post;
1109 let put = HttpMethod::Put;
1110 let delete = HttpMethod::Delete;
1111
1112 #[cfg(not(feature = "preserve_path_order"))]
1113 {
1114 let expected_value = vec![
1115 ("/todo", &get),
1116 ("/todo", &post),
1117 ("/todo/search", &get),
1118 ("/todo/{id}", &get),
1119 ("/todo/{id}", &put),
1120 ("/todo/{id}", &delete),
1121 ];
1122 assert_eq!(actual_value, expected_value);
1123 }
1124
1125 #[cfg(feature = "preserve_path_order")]
1126 {
1127 let expected_value = vec![
1128 ("/todo", &get),
1129 ("/todo", &post),
1130 ("/todo/{id}", &get),
1131 ("/todo/{id}", &put),
1132 ("/todo/{id}", &delete),
1133 ("/todo/search", &get),
1134 ];
1135 assert_eq!(actual_value, expected_value);
1136 }
1137 }
1138
1139 #[test]
1140 fn operation_new() {
1141 let operation = Operation::new();
1142
1143 assert!(operation.tags.is_none());
1144 assert!(operation.summary.is_none());
1145 assert!(operation.description.is_none());
1146 assert!(operation.operation_id.is_none());
1147 assert!(operation.external_docs.is_none());
1148 assert!(operation.parameters.is_none());
1149 assert!(operation.request_body.is_none());
1150 assert!(operation.responses.responses.is_empty());
1151 assert!(operation.callbacks.is_empty());
1152 assert!(operation.deprecated.is_none());
1153 assert!(operation.security.is_none());
1154 assert!(operation.servers.is_none());
1155 }
1156
1157 #[test]
1158 fn operation_builder_security() {
1159 let security_requirement1 =
1160 SecurityRequirement::new("api_oauth2_flow", ["edit:items", "read:items"]);
1161 let security_requirement2 = SecurityRequirement::new("api_oauth2_flow", ["remove:items"]);
1162 let operation = OperationBuilder::new()
1163 .security(security_requirement1)
1164 .security(security_requirement2)
1165 .build();
1166
1167 assert!(operation.security.is_some());
1168 }
1169
1170 #[test]
1171 fn operation_builder_server() {
1172 let server1 = Server::new("/api");
1173 let server2 = Server::new("/admin");
1174 let operation = OperationBuilder::new()
1175 .server(server1)
1176 .server(server2)
1177 .build();
1178
1179 assert!(operation.servers.is_some());
1180 }
1181}