1use crate::Path;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use super::{
9 builder,
10 extensions::Extensions,
11 request_body::RequestBody,
12 response::{Response, Responses},
13 security::SecurityRequirement,
14 set_value, Deprecated, ExternalDocs, RefOr, Required, Schema, Server,
15};
16
17#[cfg(not(feature = "preserve_path_order"))]
18#[allow(missing_docs)]
19#[doc(hidden)]
20pub type PathsMap<K, V> = std::collections::BTreeMap<K, V>;
21#[cfg(feature = "preserve_path_order")]
22#[allow(missing_docs)]
23#[doc(hidden)]
24pub type PathsMap<K, V> = indexmap::IndexMap<K, V>;
25
26builder! {
27 PathsBuilder;
28
29 #[non_exhaustive]
36 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
37 #[cfg_attr(feature = "debug", derive(Debug))]
38 pub struct Paths {
39 #[serde(flatten)]
42 pub paths: PathsMap<String, PathItem>,
43
44 #[serde(skip_serializing_if = "Option::is_none", flatten)]
46 pub extensions: Option<Extensions>,
47 }
48}
49
50impl Paths {
51 pub fn new() -> Self {
53 Default::default()
54 }
55
56 pub fn get_path_item<P: AsRef<str>>(&self, path: P) -> Option<&PathItem> {
68 self.paths.get(path.as_ref())
69 }
70
71 pub fn get_path_operation<P: AsRef<str>>(
85 &self,
86 path: P,
87 http_method: HttpMethod,
88 ) -> Option<&Operation> {
89 self.paths
90 .get(path.as_ref())
91 .and_then(|path| match http_method {
92 HttpMethod::Get => path.get.as_ref(),
93 HttpMethod::Put => path.put.as_ref(),
94 HttpMethod::Post => path.post.as_ref(),
95 HttpMethod::Delete => path.delete.as_ref(),
96 HttpMethod::Options => path.options.as_ref(),
97 HttpMethod::Head => path.head.as_ref(),
98 HttpMethod::Patch => path.patch.as_ref(),
99 HttpMethod::Trace => path.trace.as_ref(),
100 })
101 }
102
103 pub fn add_path_operation<P: AsRef<str>, O: Into<Operation>>(
111 &mut self,
112 path: P,
113 http_methods: Vec<HttpMethod>,
114 operation: O,
115 ) {
116 let path = path.as_ref();
117 let operation = operation.into();
118 if let Some(existing_item) = self.paths.get_mut(path) {
119 for http_method in http_methods {
120 match http_method {
121 HttpMethod::Get => existing_item.get = Some(operation.clone()),
122 HttpMethod::Put => existing_item.put = Some(operation.clone()),
123 HttpMethod::Post => existing_item.post = Some(operation.clone()),
124 HttpMethod::Delete => existing_item.delete = Some(operation.clone()),
125 HttpMethod::Options => existing_item.options = Some(operation.clone()),
126 HttpMethod::Head => existing_item.head = Some(operation.clone()),
127 HttpMethod::Patch => existing_item.patch = Some(operation.clone()),
128 HttpMethod::Trace => existing_item.trace = Some(operation.clone()),
129 };
130 }
131 } else {
132 self.paths.insert(
133 String::from(path),
134 PathItem::from_http_methods(http_methods, operation),
135 );
136 }
137 }
138
139 pub fn merge(&mut self, other_paths: Paths) {
143 for (path, that) in other_paths.paths {
144 if let Some(this) = self.paths.get_mut(&path) {
145 this.merge_operations(that);
146 } else {
147 self.paths.insert(path, that);
148 }
149 }
150
151 if let Some(other_paths_extensions) = other_paths.extensions {
152 let paths_extensions = self.extensions.get_or_insert(Extensions::default());
153 paths_extensions.merge(other_paths_extensions);
154 }
155 }
156}
157
158impl PathsBuilder {
159 pub fn path<I: Into<String>>(mut self, path: I, item: PathItem) -> Self {
162 let path_string = path.into();
163 if let Some(existing_item) = self.paths.get_mut(&path_string) {
164 existing_item.merge_operations(item);
165 } else {
166 self.paths.insert(path_string, item);
167 }
168
169 self
170 }
171
172 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
174 set_value!(self extensions extensions)
175 }
176
177 pub fn path_from<P: Path>(self) -> Self {
196 let methods = P::methods();
197 let operation = P::operation();
198
199 let path_item = if methods.len() == 1 {
201 PathItem::new(
202 methods
203 .into_iter()
204 .next()
205 .expect("must have one operation method"),
206 operation,
207 )
208 } else {
209 methods
210 .into_iter()
211 .fold(PathItemBuilder::new(), |path_item, method| {
212 path_item.operation(method, operation.clone())
213 })
214 .build()
215 };
216
217 self.path(P::path(), path_item)
218 }
219}
220
221builder! {
222 PathItemBuilder;
223
224 #[non_exhaustive]
229 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
230 #[cfg_attr(feature = "debug", derive(Debug))]
231 #[serde(rename_all = "camelCase")]
232 pub struct PathItem {
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub summary: Option<String>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
240 pub description: Option<String>,
241
242 #[serde(skip_serializing_if = "Option::is_none")]
245 pub servers: Option<Vec<Server>>,
246
247 #[serde(skip_serializing_if = "Option::is_none")]
251 pub parameters: Option<Vec<Parameter>>,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub get: Option<Operation>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub put: Option<Operation>,
260
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub post: Option<Operation>,
264
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub delete: Option<Operation>,
268
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub options: Option<Operation>,
272
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub head: Option<Operation>,
276
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub patch: Option<Operation>,
280
281 #[serde(skip_serializing_if = "Option::is_none")]
283 pub trace: Option<Operation>,
284
285 #[serde(skip_serializing_if = "Option::is_none", flatten)]
287 pub extensions: Option<Extensions>,
288 }
289}
290
291impl PathItem {
292 pub fn new<O: Into<Operation>>(http_method: HttpMethod, operation: O) -> Self {
294 let mut path_item = Self::default();
295 match http_method {
296 HttpMethod::Get => path_item.get = Some(operation.into()),
297 HttpMethod::Put => path_item.put = Some(operation.into()),
298 HttpMethod::Post => path_item.post = Some(operation.into()),
299 HttpMethod::Delete => path_item.delete = Some(operation.into()),
300 HttpMethod::Options => path_item.options = Some(operation.into()),
301 HttpMethod::Head => path_item.head = Some(operation.into()),
302 HttpMethod::Patch => path_item.patch = Some(operation.into()),
303 HttpMethod::Trace => path_item.trace = Some(operation.into()),
304 };
305
306 path_item
307 }
308
309 pub fn from_http_methods<I: IntoIterator<Item = HttpMethod>, O: Into<Operation>>(
311 http_methods: I,
312 operation: O,
313 ) -> Self {
314 let mut path_item = Self::default();
315 let operation = operation.into();
316 for method in http_methods {
317 match method {
318 HttpMethod::Get => path_item.get = Some(operation.clone()),
319 HttpMethod::Put => path_item.put = Some(operation.clone()),
320 HttpMethod::Post => path_item.post = Some(operation.clone()),
321 HttpMethod::Delete => path_item.delete = Some(operation.clone()),
322 HttpMethod::Options => path_item.options = Some(operation.clone()),
323 HttpMethod::Head => path_item.head = Some(operation.clone()),
324 HttpMethod::Patch => path_item.patch = Some(operation.clone()),
325 HttpMethod::Trace => path_item.trace = Some(operation.clone()),
326 };
327 }
328
329 path_item
330 }
331
332 pub fn merge_operations(&mut self, path_item: PathItem) {
335 if path_item.get.is_some() && self.get.is_none() {
336 self.get = path_item.get;
337 }
338 if path_item.put.is_some() && self.put.is_none() {
339 self.put = path_item.put;
340 }
341 if path_item.post.is_some() && self.post.is_none() {
342 self.post = path_item.post;
343 }
344 if path_item.delete.is_some() && self.delete.is_none() {
345 self.delete = path_item.delete;
346 }
347 if path_item.options.is_some() && self.options.is_none() {
348 self.options = path_item.options;
349 }
350 if path_item.head.is_some() && self.head.is_none() {
351 self.head = path_item.head;
352 }
353 if path_item.patch.is_some() && self.patch.is_none() {
354 self.patch = path_item.patch;
355 }
356 if path_item.trace.is_some() && self.trace.is_none() {
357 self.trace = path_item.trace;
358 }
359 }
360}
361
362impl PathItemBuilder {
363 pub fn operation<O: Into<Operation>>(mut self, http_method: HttpMethod, operation: O) -> Self {
366 match http_method {
367 HttpMethod::Get => self.get = Some(operation.into()),
368 HttpMethod::Put => self.put = Some(operation.into()),
369 HttpMethod::Post => self.post = Some(operation.into()),
370 HttpMethod::Delete => self.delete = Some(operation.into()),
371 HttpMethod::Options => self.options = Some(operation.into()),
372 HttpMethod::Head => self.head = Some(operation.into()),
373 HttpMethod::Patch => self.patch = Some(operation.into()),
374 HttpMethod::Trace => self.trace = Some(operation.into()),
375 };
376
377 self
378 }
379
380 pub fn summary<S: Into<String>>(mut self, summary: Option<S>) -> Self {
382 set_value!(self summary summary.map(|summary| summary.into()))
383 }
384
385 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
388 set_value!(self description description.map(|description| description.into()))
389 }
390
391 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: Option<I>) -> Self {
394 set_value!(self servers servers.map(|servers| servers.into_iter().collect()))
395 }
396
397 pub fn parameters<I: IntoIterator<Item = Parameter>>(mut self, parameters: Option<I>) -> Self {
399 set_value!(self parameters parameters.map(|parameters| parameters.into_iter().collect()))
400 }
401
402 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
404 set_value!(self extensions extensions)
405 }
406}
407
408#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
412#[serde(rename_all = "lowercase")]
413#[cfg_attr(feature = "debug", derive(Debug))]
414pub enum HttpMethod {
415 Get,
417 Post,
419 Put,
421 Delete,
423 Options,
425 Head,
427 Patch,
429 Trace,
431}
432
433builder! {
434 OperationBuilder;
435
436 #[non_exhaustive]
440 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
441 #[cfg_attr(feature = "debug", derive(Debug))]
442 #[serde(rename_all = "camelCase")]
443 pub struct Operation {
444 #[serde(skip_serializing_if = "Option::is_none")]
454 pub tags: Option<Vec<String>>,
455
456 #[serde(skip_serializing_if = "Option::is_none")]
463 pub summary: Option<String>,
464
465 #[serde(skip_serializing_if = "Option::is_none")]
472 pub description: Option<String>,
473
474 #[serde(skip_serializing_if = "Option::is_none")]
481 pub operation_id: Option<String>,
482
483 #[serde(skip_serializing_if = "Option::is_none")]
485 pub external_docs: Option<ExternalDocs>,
486
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub parameters: Option<Vec<Parameter>>,
490
491 #[serde(skip_serializing_if = "Option::is_none")]
493 pub request_body: Option<RequestBody>,
494
495 pub responses: Responses,
497
498 #[allow(missing_docs)]
500 #[serde(skip_serializing_if = "Option::is_none")]
501 pub callbacks: Option<String>,
502
503 #[serde(skip_serializing_if = "Option::is_none")]
505 pub deprecated: Option<Deprecated>,
506
507 #[serde(skip_serializing_if = "Option::is_none")]
513 pub security: Option<Vec<SecurityRequirement>>,
514
515 #[serde(skip_serializing_if = "Option::is_none")]
517 pub servers: Option<Vec<Server>>,
518
519 #[serde(skip_serializing_if = "Option::is_none", flatten)]
521 pub extensions: Option<Extensions>,
522 }
523}
524
525impl Operation {
526 pub fn new() -> Self {
528 Default::default()
529 }
530}
531
532impl OperationBuilder {
533 pub fn tags<I: IntoIterator<Item = V>, V: Into<String>>(mut self, tags: Option<I>) -> Self {
535 set_value!(self tags tags.map(|tags| tags.into_iter().map(Into::into).collect()))
536 }
537
538 pub fn tag<S: Into<String>>(mut self, tag: S) -> Self {
540 let tag_string = tag.into();
541 match self.tags {
542 Some(ref mut tags) => tags.push(tag_string),
543 None => {
544 self.tags = Some(vec![tag_string]);
545 }
546 }
547
548 self
549 }
550
551 pub fn summary<S: Into<String>>(mut self, summary: Option<S>) -> Self {
553 set_value!(self summary summary.map(|summary| summary.into()))
554 }
555
556 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
558 set_value!(self description description.map(|description| description.into()))
559 }
560
561 pub fn operation_id<S: Into<String>>(mut self, operation_id: Option<S>) -> Self {
563 set_value!(self operation_id operation_id.map(|operation_id| operation_id.into()))
564 }
565
566 pub fn parameters<I: IntoIterator<Item = P>, P: Into<Parameter>>(
568 mut self,
569 parameters: Option<I>,
570 ) -> Self {
571 self.parameters = parameters.map(|parameters| {
572 if let Some(mut params) = self.parameters {
573 params.extend(parameters.into_iter().map(|parameter| parameter.into()));
574 params
575 } else {
576 parameters
577 .into_iter()
578 .map(|parameter| parameter.into())
579 .collect()
580 }
581 });
582
583 self
584 }
585
586 pub fn parameter<P: Into<Parameter>>(mut self, parameter: P) -> Self {
588 match self.parameters {
589 Some(ref mut parameters) => parameters.push(parameter.into()),
590 None => {
591 self.parameters = Some(vec![parameter.into()]);
592 }
593 }
594
595 self
596 }
597
598 pub fn request_body(mut self, request_body: Option<RequestBody>) -> Self {
600 set_value!(self request_body request_body)
601 }
602
603 pub fn responses<R: Into<Responses>>(mut self, responses: R) -> Self {
605 set_value!(self responses responses.into())
606 }
607
608 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
613 mut self,
614 code: S,
615 response: R,
616 ) -> Self {
617 self.responses
618 .responses
619 .insert(code.into(), response.into());
620
621 self
622 }
623
624 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
626 set_value!(self deprecated deprecated)
627 }
628
629 pub fn securities<I: IntoIterator<Item = SecurityRequirement>>(
631 mut self,
632 securities: Option<I>,
633 ) -> Self {
634 set_value!(self security securities.map(|securities| securities.into_iter().collect()))
635 }
636
637 pub fn security(mut self, security: SecurityRequirement) -> Self {
639 if let Some(ref mut securities) = self.security {
640 securities.push(security);
641 } else {
642 self.security = Some(vec![security]);
643 }
644
645 self
646 }
647
648 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: Option<I>) -> Self {
650 set_value!(self servers servers.map(|servers| servers.into_iter().collect()))
651 }
652
653 pub fn server(mut self, server: Server) -> Self {
655 if let Some(ref mut servers) = self.servers {
656 servers.push(server);
657 } else {
658 self.servers = Some(vec![server]);
659 }
660
661 self
662 }
663
664 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
666 set_value!(self extensions extensions)
667 }
668}
669
670builder! {
671 ParameterBuilder;
672
673 #[non_exhaustive]
677 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
678 #[cfg_attr(feature = "debug", derive(Debug))]
679 #[serde(rename_all = "camelCase")]
680 pub struct Parameter {
681 pub name: String,
686
687 #[serde(rename = "in")]
689 pub parameter_in: ParameterIn,
690
691 #[serde(skip_serializing_if = "Option::is_none")]
693 pub description: Option<String>,
694
695 pub required: Required,
699
700 #[serde(skip_serializing_if = "Option::is_none")]
702 pub deprecated: Option<Deprecated>,
703 #[serde(skip_serializing_if = "Option::is_none")]
706 pub schema: Option<RefOr<Schema>>,
707
708 #[serde(skip_serializing_if = "Option::is_none")]
711 pub style: Option<ParameterStyle>,
712
713 #[serde(skip_serializing_if = "Option::is_none")]
726 pub explode: Option<bool>,
727
728 #[serde(skip_serializing_if = "Option::is_none")]
732 pub allow_reserved: Option<bool>,
733
734 #[serde(skip_serializing_if = "Option::is_none")]
737 example: Option<Value>,
738
739 #[serde(skip_serializing_if = "Option::is_none", flatten)]
741 pub extensions: Option<Extensions>,
742 }
743}
744
745impl Parameter {
746 pub fn new<S: Into<String>>(name: S) -> Self {
748 Self {
749 name: name.into(),
750 required: Required::True,
751 ..Default::default()
752 }
753 }
754}
755
756impl ParameterBuilder {
757 pub fn name<I: Into<String>>(mut self, name: I) -> Self {
759 set_value!(self name name.into())
760 }
761
762 pub fn parameter_in(mut self, parameter_in: ParameterIn) -> Self {
764 set_value!(self parameter_in parameter_in)
765 }
766
767 pub fn required(mut self, required: Required) -> Self {
770 self.required = required;
771 if self.parameter_in == ParameterIn::Path {
773 self.required = Required::True;
774 }
775
776 self
777 }
778
779 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
781 set_value!(self description description.map(|description| description.into()))
782 }
783
784 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
786 set_value!(self deprecated deprecated)
787 }
788
789 pub fn schema<I: Into<RefOr<Schema>>>(mut self, component: Option<I>) -> Self {
791 set_value!(self schema component.map(|component| component.into()))
792 }
793
794 pub fn style(mut self, style: Option<ParameterStyle>) -> Self {
796 set_value!(self style style)
797 }
798
799 pub fn explode(mut self, explode: Option<bool>) -> Self {
801 set_value!(self explode explode)
802 }
803
804 pub fn allow_reserved(mut self, allow_reserved: Option<bool>) -> Self {
806 set_value!(self allow_reserved allow_reserved)
807 }
808
809 pub fn example(mut self, example: Option<Value>) -> Self {
811 set_value!(self example example)
812 }
813
814 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
816 set_value!(self extensions extensions)
817 }
818}
819
820#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
822#[serde(rename_all = "lowercase")]
823#[cfg_attr(feature = "debug", derive(Debug))]
824pub enum ParameterIn {
825 Query,
827 Path,
829 Header,
831 Cookie,
833}
834
835impl Default for ParameterIn {
836 fn default() -> Self {
837 Self::Path
838 }
839}
840
841#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
843#[cfg_attr(feature = "debug", derive(Debug))]
844#[serde(rename_all = "camelCase")]
845pub enum ParameterStyle {
846 Matrix,
850 Label,
854 Form,
858 Simple,
861 SpaceDelimited,
864 PipeDelimited,
867 DeepObject,
870}
871
872#[cfg(test)]
873mod tests {
874 use super::{HttpMethod, Operation, OperationBuilder};
875 use crate::openapi::{security::SecurityRequirement, server::Server, PathItem, PathsBuilder};
876
877 #[test]
878 fn test_path_order() {
879 let paths_list = PathsBuilder::new()
880 .path(
881 "/todo",
882 PathItem::new(HttpMethod::Get, OperationBuilder::new()),
883 )
884 .path(
885 "/todo",
886 PathItem::new(HttpMethod::Post, OperationBuilder::new()),
887 )
888 .path(
889 "/todo/{id}",
890 PathItem::new(HttpMethod::Delete, OperationBuilder::new()),
891 )
892 .path(
893 "/todo/{id}",
894 PathItem::new(HttpMethod::Get, OperationBuilder::new()),
895 )
896 .path(
897 "/todo/{id}",
898 PathItem::new(HttpMethod::Put, OperationBuilder::new()),
899 )
900 .path(
901 "/todo/search",
902 PathItem::new(HttpMethod::Get, OperationBuilder::new()),
903 )
904 .build();
905
906 let actual_value = paths_list
907 .paths
908 .iter()
909 .flat_map(|(path, path_item)| {
910 let mut path_methods =
911 Vec::<(&str, &HttpMethod)>::with_capacity(paths_list.paths.len());
912 if path_item.get.is_some() {
913 path_methods.push((path, &HttpMethod::Get));
914 }
915 if path_item.put.is_some() {
916 path_methods.push((path, &HttpMethod::Put));
917 }
918 if path_item.post.is_some() {
919 path_methods.push((path, &HttpMethod::Post));
920 }
921 if path_item.delete.is_some() {
922 path_methods.push((path, &HttpMethod::Delete));
923 }
924 if path_item.options.is_some() {
925 path_methods.push((path, &HttpMethod::Options));
926 }
927 if path_item.head.is_some() {
928 path_methods.push((path, &HttpMethod::Head));
929 }
930 if path_item.patch.is_some() {
931 path_methods.push((path, &HttpMethod::Patch));
932 }
933 if path_item.trace.is_some() {
934 path_methods.push((path, &HttpMethod::Trace));
935 }
936
937 path_methods
938 })
939 .collect::<Vec<_>>();
940
941 let get = HttpMethod::Get;
942 let post = HttpMethod::Post;
943 let put = HttpMethod::Put;
944 let delete = HttpMethod::Delete;
945
946 #[cfg(not(feature = "preserve_path_order"))]
947 {
948 let expected_value = vec![
949 ("/todo", &get),
950 ("/todo", &post),
951 ("/todo/search", &get),
952 ("/todo/{id}", &get),
953 ("/todo/{id}", &put),
954 ("/todo/{id}", &delete),
955 ];
956 assert_eq!(actual_value, expected_value);
957 }
958
959 #[cfg(feature = "preserve_path_order")]
960 {
961 let expected_value = vec![
962 ("/todo", &get),
963 ("/todo", &post),
964 ("/todo/{id}", &get),
965 ("/todo/{id}", &put),
966 ("/todo/{id}", &delete),
967 ("/todo/search", &get),
968 ];
969 assert_eq!(actual_value, expected_value);
970 }
971 }
972
973 #[test]
974 fn operation_new() {
975 let operation = Operation::new();
976
977 assert!(operation.tags.is_none());
978 assert!(operation.summary.is_none());
979 assert!(operation.description.is_none());
980 assert!(operation.operation_id.is_none());
981 assert!(operation.external_docs.is_none());
982 assert!(operation.parameters.is_none());
983 assert!(operation.request_body.is_none());
984 assert!(operation.responses.responses.is_empty());
985 assert!(operation.callbacks.is_none());
986 assert!(operation.deprecated.is_none());
987 assert!(operation.security.is_none());
988 assert!(operation.servers.is_none());
989 }
990
991 #[test]
992 fn operation_builder_security() {
993 let security_requirement1 =
994 SecurityRequirement::new("api_oauth2_flow", ["edit:items", "read:items"]);
995 let security_requirement2 = SecurityRequirement::new("api_oauth2_flow", ["remove:items"]);
996 let operation = OperationBuilder::new()
997 .security(security_requirement1)
998 .security(security_requirement2)
999 .build();
1000
1001 assert!(operation.security.is_some());
1002 }
1003
1004 #[test]
1005 fn operation_builder_server() {
1006 let server1 = Server::new("/api");
1007 let server2 = Server::new("/admin");
1008 let operation = OperationBuilder::new()
1009 .server(server1)
1010 .server(server2)
1011 .build();
1012
1013 assert!(operation.servers.is_some());
1014 }
1015}