Skip to main content

autapi/openapi/
mod.rs

1mod macros;
2mod r#ref;
3mod schema;
4
5use std::{
6    collections::{BTreeMap, btree_map},
7    ops::{Deref, DerefMut},
8};
9
10use http::Method;
11use serde::{Deserialize, Serialize, ser::SerializeMap};
12use serde_json::Value;
13
14pub use self::{r#ref::*, schema::*};
15use crate::private::{is_default, merge_maps};
16
17pub type Map<K, V> = BTreeMap<K, V>;
18pub type Entry<'a, K, V> = btree_map::Entry<'a, K, V>;
19
20macros::define_openapi_spec_object! {
21    pub struct OpenApi {
22        pub openapi: String,
23        pub info: Info,
24        pub json_schema_dialect: Option<String>,
25        #[serde(default, skip_serializing_if = "Vec::is_empty")]
26        pub servers: Vec<Server>,
27        #[serde(default, skip_serializing_if = "is_default")]
28        pub paths: Paths,
29        #[serde(default, skip_serializing_if = "Map::is_empty")]
30        pub webhooks: Map<String, PathItem>,
31        #[serde(default, skip_serializing_if = "is_default")]
32        pub components: Components,
33        pub security: Option<Vec<SecurityRequirement>>,
34        #[serde(default, skip_serializing_if = "Vec::is_empty")]
35        pub tags: Vec<Tag>,
36        pub external_docs: Option<ExternalDocs>,
37
38        #[serde(flatten)]
39        pub extensions: Extensions,
40    }
41}
42
43impl OpenApi {
44    pub const DEFAULT_VERSION: &str = "3.1.0";
45
46    pub fn new(version: String, info: Info) -> Self {
47        Self {
48            openapi: version,
49            info,
50            json_schema_dialect: Default::default(),
51            servers: Default::default(),
52            paths: Default::default(),
53            webhooks: Default::default(),
54            components: Default::default(),
55            security: Default::default(),
56            tags: Default::default(),
57            external_docs: Default::default(),
58            extensions: Default::default(),
59        }
60    }
61
62    pub fn operations_mut(&mut self) -> impl Iterator<Item = &mut Operation> {
63        self.paths
64            .paths
65            .values_mut()
66            .flat_map(|item| item.operations_mut())
67    }
68}
69
70impl Default for OpenApi {
71    fn default() -> Self {
72        Self::new(Self::DEFAULT_VERSION.to_owned(), Info::default())
73    }
74}
75
76macros::define_openapi_spec_object! {
77    #[derive(Default)]
78    pub struct Info {
79        pub title: String,
80        pub summary: Option<String>,
81        pub description: Option<String>,
82        pub terms_of_service: Option<String>,
83        pub contact: Option<Contact>,
84        pub license: Option<License>,
85        pub version: String,
86
87        #[serde(flatten)]
88        pub extensions: Extensions,
89    }
90}
91
92impl Info {
93    pub fn new(title: String) -> Self {
94        Self::default().with_title(title)
95    }
96}
97
98#[macro_export]
99macro_rules! info_from_env {
100    () => {
101        $crate::openapi::Info::default()
102            .with_title(env!("CARGO_PKG_NAME"))
103            .with_version(env!("CARGO_PKG_VERSION"))
104            .with_maybe_description(option_env!("CARGO_PKG_DESCRIPTION").map(ToOwned::to_owned))
105            .with_maybe_license(option_env!("CARGO_PKG_LICENSE").map(|license| {
106                $crate::openapi::License::new(license.to_owned())
107                    .with_identifier(license.to_owned())
108            }))
109    };
110}
111
112macros::define_openapi_spec_object! {
113    #[derive(Default)]
114    pub struct Contact {
115        pub name: Option<String>,
116        pub url: Option<String>,
117        pub email: Option<String>,
118
119        #[serde(flatten)]
120        pub extensions: Extensions,
121    }
122}
123
124macros::define_openapi_spec_object! {
125    pub struct License {
126        pub name: String,
127        pub identifier: Option<String>,
128        pub url: Option<String>,
129
130        #[serde(flatten)]
131        pub extensions: Extensions,
132    }
133}
134
135impl License {
136    pub fn new(name: String) -> Self {
137        Self {
138            name,
139            identifier: Default::default(),
140            url: Default::default(),
141            extensions: Default::default(),
142        }
143    }
144}
145
146macros::define_openapi_spec_object! {
147    pub struct Server {
148        pub url: String,
149        pub description: Option<String>,
150        #[serde(default, skip_serializing_if = "Map::is_empty")]
151        pub variables: Map<String, ServerVariable>,
152
153        #[serde(flatten)]
154        pub extensions: Extensions,
155    }
156}
157
158impl Server {
159    pub fn new(url: String) -> Self {
160        Server {
161            url,
162            description: Default::default(),
163            variables: Default::default(),
164            extensions: Default::default(),
165        }
166    }
167}
168
169macros::define_openapi_spec_object! {
170    pub struct ServerVariable {
171        pub r#enum: Vec<String>,
172        pub default: String,
173        pub description: Option<String>,
174
175        #[serde(flatten)]
176        pub extensions: Extensions,
177    }
178}
179
180impl ServerVariable {
181    pub fn new(r#enum: Vec<String>, default: String) -> Self {
182        Self {
183            r#enum,
184            default,
185            description: Default::default(),
186            extensions: Default::default(),
187        }
188    }
189}
190
191macros::define_openapi_spec_object! {
192    #[derive(Default)]
193    pub struct Components {
194        #[serde(default, skip_serializing_if = "Map::is_empty")]
195        pub schemas: Map<String, MaybeRef<Schema>>,
196        #[serde(default, skip_serializing_if = "Map::is_empty")]
197        pub responses: Map<String, MaybeRef<Response>>,
198        #[serde(default, skip_serializing_if = "Map::is_empty")]
199        pub parameters: Map<String, MaybeRef<Parameter>>,
200        #[serde(default, skip_serializing_if = "Map::is_empty")]
201        pub examples: Map<String, MaybeRef<Parameter>>,
202        #[serde(default, skip_serializing_if = "Map::is_empty")]
203        pub request_bodies: Map<String, MaybeRef<RequestBody>>,
204        #[serde(default, skip_serializing_if = "Map::is_empty")]
205        pub headers: Map<String, MaybeRef<Header>>,
206        #[serde(default, skip_serializing_if = "Map::is_empty")]
207        pub security_schemes: Map<String, MaybeRef<SecurityScheme>>,
208        #[serde(default, skip_serializing_if = "Map::is_empty")]
209        pub links: Map<String, MaybeRef<Link>>,
210        #[serde(default, skip_serializing_if = "Map::is_empty")]
211        pub callbacks: Map<String, MaybeRef<Callback>>,
212        #[serde(default, skip_serializing_if = "Map::is_empty")]
213        pub path_items: Map<String, PathItem>,
214
215        #[serde(flatten)]
216        pub extensions: Extensions,
217    }
218}
219
220impl Components {
221    pub fn resolve_schema<'a>(&'a self, mut schema: &'a MaybeRef<Schema>) -> Option<&'a Schema> {
222        loop {
223            match schema {
224                MaybeRef::T(schema) => break Some(schema),
225                MaybeRef::Ref(r) => {
226                    let name = r.get_components_schema_name()?;
227                    schema = self.schemas.get(name)?;
228                }
229            }
230        }
231    }
232}
233
234macros::define_openapi_spec_object! {
235    #[derive(Default)]
236    pub struct Paths {
237        #[serde(flatten)]
238        pub paths: Map<String, PathItem>,
239
240        #[serde(flatten)]
241        pub extensions: Extensions,
242    }
243}
244
245macros::define_openapi_spec_object! {
246    #[derive(Default)]
247    pub struct PathItem {
248        #[serde(rename = "$ref")]
249        pub r#ref: Option<String>,
250        pub summary: Option<String>,
251        pub description: Option<String>,
252        pub get: Option<Operation>,
253        pub put: Option<Operation>,
254        pub post: Option<Operation>,
255        pub delete: Option<Operation>,
256        pub options: Option<Operation>,
257        pub head: Option<Operation>,
258        pub patch: Option<Operation>,
259        pub trace: Option<Operation>,
260        #[serde(default, skip_serializing_if = "Vec::is_empty")]
261        pub servers: Vec<Server>,
262        #[serde(default, skip_serializing_if = "Vec::is_empty")]
263        pub parameters: Vec<MaybeRef<Parameter>>,
264
265        #[serde(flatten)]
266        pub extensions: Extensions,
267    }
268}
269
270impl PathItem {
271    pub fn operation_by_method_mut(&mut self, method: Method) -> Option<&mut Option<Operation>> {
272        Some(match method {
273            Method::GET => &mut self.get,
274            Method::PUT => &mut self.put,
275            Method::POST => &mut self.post,
276            Method::DELETE => &mut self.delete,
277            Method::OPTIONS => &mut self.options,
278            Method::HEAD => &mut self.head,
279            Method::PATCH => &mut self.patch,
280            Method::TRACE => &mut self.trace,
281            _ => return None,
282        })
283    }
284    pub fn operations_mut(&mut self) -> impl Iterator<Item = &mut Operation> {
285        [
286            &mut self.delete,
287            &mut self.get,
288            &mut self.head,
289            &mut self.options,
290            &mut self.patch,
291            &mut self.post,
292            &mut self.put,
293            &mut self.trace,
294        ]
295        .into_iter()
296        .filter_map(|op| op.as_mut())
297    }
298}
299
300macros::define_openapi_spec_object! {
301    #[derive(Default)]
302    pub struct Operation {
303        #[serde(default, skip_serializing_if = "Vec::is_empty")]
304        pub tags: Vec<String>,
305        pub summary: Option<String>,
306        pub description: Option<String>,
307        pub external_docs: Option<ExternalDocs>,
308        pub operation_id: Option<String>,
309        #[serde(default, skip_serializing_if = "Vec::is_empty")]
310        pub parameters: Vec<MaybeRef<Parameter>>,
311        pub request_body: Option<MaybeRef<RequestBody>>,
312        #[serde(default, skip_serializing_if = "Responses::is_empty")]
313        pub responses: Responses,
314        #[serde(default, skip_serializing_if = "Map::is_empty")]
315        pub callbacks: Map<String, MaybeRef<Callback>>,
316        pub deprecated: Option<bool>,
317        pub security: Option<Vec<SecurityRequirement>>,
318        #[serde(default, skip_serializing_if = "Vec::is_empty")]
319        pub servers: Vec<Server>,
320
321        #[serde(flatten)]
322        pub extensions: Extensions,
323    }
324}
325
326impl Operation {
327    pub fn add_response(&mut self, status_code: String, response: MaybeRef<Response>) {
328        match self.responses.responses.entry(status_code) {
329            Entry::Occupied(entry) => {
330                panic!("colliding responses for status code {}", entry.key());
331            }
332            Entry::Vacant(entry) => {
333                entry.insert(response);
334            }
335        }
336    }
337}
338
339macros::define_openapi_spec_object! {
340    pub struct ExternalDocs {
341        pub description: Option<String>,
342        pub url: String,
343
344        #[serde(flatten)]
345        pub extensions: Extensions,
346    }
347}
348
349impl ExternalDocs {
350    pub fn new(url: String) -> Self {
351        Self {
352            url,
353            description: Default::default(),
354            extensions: Default::default(),
355        }
356    }
357}
358
359macros::define_openapi_spec_object! {
360    pub struct Parameter {
361        pub name: String,
362        pub r#in: ParameterIn,
363        pub description: Option<String>,
364        pub required: Option<bool>,
365        pub deprecated: Option<bool>,
366        pub allow_empty_value: Option<bool>,
367        pub style: Option<String>,
368        pub explode: Option<bool>,
369        pub allow_reserved: Option<bool>,
370        pub schema: Option<MaybeRef<Schema>>,
371        pub example: Option<Value>,
372        #[serde(default, skip_serializing_if = "Map::is_empty")]
373        pub examples: Map<String, MaybeRef<Example>>,
374        #[serde(default, skip_serializing_if = "Map::is_empty")]
375        pub content: Map<String, MediaTypeContent>,
376
377        #[serde(flatten)]
378        pub extensions: Extensions,
379    }
380}
381
382#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
383#[serde(rename_all = "camelCase")]
384#[non_exhaustive]
385pub enum ParameterIn {
386    Query,
387    Header,
388    Path,
389    Cookie,
390}
391
392impl Parameter {
393    pub fn new(name: String, r#in: ParameterIn) -> Self {
394        Self {
395            name,
396            r#in,
397            description: Default::default(),
398            required: Default::default(),
399            deprecated: Default::default(),
400            allow_empty_value: Default::default(),
401            style: Default::default(),
402            explode: Default::default(),
403            allow_reserved: Default::default(),
404            schema: Default::default(),
405            example: Default::default(),
406            examples: Default::default(),
407            content: Default::default(),
408            extensions: Default::default(),
409        }
410    }
411}
412
413macros::define_openapi_spec_object! {
414    #[derive(Default)]
415    pub struct RequestBody {
416        pub description: Option<String>,
417        pub content: Map<String, MediaTypeContent>,
418        pub required: Option<bool>,
419
420        #[serde(flatten)]
421        pub extensions: Extensions,
422    }
423}
424
425impl RequestBody {
426    pub fn new(content: Map<String, MediaTypeContent>) -> Self {
427        Self {
428            content,
429            description: Default::default(),
430            required: Default::default(),
431            extensions: Default::default(),
432        }
433    }
434}
435
436macros::define_openapi_spec_object! {
437    #[derive(Default)]
438    pub struct MediaTypeContent {
439        pub schema: Option<MaybeRef<Schema>>,
440        pub example: Option<Value>,
441        #[serde(default, skip_serializing_if = "Map::is_empty")]
442        pub examples: Map<String, MaybeRef<Example>>,
443        #[serde(default, skip_serializing_if = "Map::is_empty")]
444        pub encoding: Map<String, Encoding>,
445
446        #[serde(flatten)]
447        pub extensions: Extensions,
448    }
449}
450
451macros::define_openapi_spec_object! {
452    #[derive(Default)]
453    pub struct Encoding {
454        pub content_type: Option<String>,
455        #[serde(default, skip_serializing_if = "Map::is_empty")]
456        pub headers: Map<String, MaybeRef<Header>>,
457
458        #[serde(flatten)]
459        pub extensions: Extensions,
460    }
461}
462
463macros::define_openapi_spec_object! {
464    #[derive(Default)]
465    pub struct Responses {
466        #[serde(flatten, default, skip_serializing_if = "Map::is_empty")]
467        pub responses: Map<String, MaybeRef<Response>>,
468
469        #[serde(flatten)]
470        pub extensions: Extensions,
471    }
472}
473
474impl Responses {
475    pub fn is_empty(&self) -> bool {
476        self.responses.is_empty() && self.extensions.is_empty()
477    }
478    pub fn merge_with(&mut self, other: Responses) {
479        for (status_code, response) in other.responses {
480            match self.responses.entry(status_code) {
481                Entry::Occupied(mut entry) => merge_response_into(entry.get_mut(), response),
482                Entry::Vacant(entry) => {
483                    entry.insert(response);
484                }
485            }
486        }
487        self.extensions.merge_with(other.extensions);
488    }
489    pub fn merge_iter(iter: impl IntoIterator<Item = Self>) -> Option<Self> {
490        let mut iter = iter.into_iter();
491        let mut responses = iter.next()?;
492        for next in iter {
493            responses.merge_with(next);
494        }
495        Some(responses)
496    }
497}
498
499/// Merges one response into another.
500///
501/// This function currently simply panics as merging responses is not supported.
502/// See issue #1 for more details.
503pub fn merge_response_into(_a: &mut MaybeRef<Response>, _b: MaybeRef<Response>) {
504    panic!("cannot merge responses");
505}
506
507/// Merges multiple responses into one or `None` if the iterator is empty.
508///
509/// This function currently simply panics for more than two responses as merging is not supported.
510/// See issue #1 for more details.
511pub fn merge_responses_iter(
512    iter: impl IntoIterator<Item = MaybeRef<Response>>,
513) -> Option<MaybeRef<Response>> {
514    let mut iter = iter.into_iter();
515    let mut response = iter.next()?;
516    for next in iter {
517        merge_response_into(&mut response, next);
518    }
519    Some(response)
520}
521
522macros::define_openapi_spec_object! {
523    #[derive(Default)]
524    pub struct Response {
525        pub description: String,
526        #[serde(default, skip_serializing_if = "Map::is_empty")]
527        pub headers: Map<String, MaybeRef<Header>>,
528        #[serde(default, skip_serializing_if = "Map::is_empty")]
529        pub content: Map<String, MediaTypeContent>,
530        #[serde(default, skip_serializing_if = "Map::is_empty")]
531        pub links: Map<String, MaybeRef<Link>>,
532
533        #[serde(flatten)]
534        pub extensions: Extensions,
535    }
536}
537
538macros::define_openapi_spec_object! {
539    #[derive(Default)]
540    pub struct Callback {
541        #[serde(flatten)]
542        pub callbacks: Map<String, PathItem>,
543
544        #[serde(flatten)]
545        pub extensions: Extensions,
546    }
547}
548
549macros::define_openapi_spec_object! {
550    #[derive(Default)]
551    pub struct Example {
552        pub summary: Option<String>,
553        pub description: Option<String>,
554        pub value: Option<Value>,
555        pub external_value: Option<String>,
556
557        #[serde(flatten)]
558        pub extensions: Extensions,
559    }
560}
561
562macros::define_openapi_spec_object! {
563    #[derive(Default)]
564    pub struct Link {
565        pub operation_ref: Option<String>,
566        pub operation_id: Option<String>,
567        #[serde(default, skip_serializing_if = "Map::is_empty")]
568        pub parameters: Map<String, Value>,
569        pub request_body: Option<Value>,
570        pub description: Option<String>,
571        pub server: Option<Server>,
572
573        #[serde(flatten)]
574        pub extensions: Extensions,
575    }
576}
577
578macros::define_openapi_spec_object! {
579    #[derive(Default)]
580    pub struct Header {
581        pub description: Option<String>,
582        pub required: Option<bool>,
583        pub deprecated: Option<bool>,
584        pub style: Option<String>,
585        pub explode: Option<bool>,
586        pub schema: Option<MaybeRef<Schema>>,
587        pub example: Option<Value>,
588        #[serde(default, skip_serializing_if = "Map::is_empty")]
589        pub examples: Map<String, MaybeRef<Example>>,
590        #[serde(default, skip_serializing_if = "Map::is_empty")]
591        pub content: Map<String, MediaTypeContent>,
592
593        #[serde(flatten)]
594        pub extensions: Extensions,
595    }
596}
597
598macros::define_openapi_spec_object! {
599    pub struct Tag {
600        pub name: String,
601        pub description: Option<String>,
602        pub external_docs: Option<ExternalDocs>,
603
604        #[serde(flatten)]
605        pub extensions: Extensions,
606    }
607}
608
609impl Tag {
610    pub fn new(name: impl Into<String>) -> Self {
611        Self {
612            name: name.into(),
613            description: Default::default(),
614            external_docs: Default::default(),
615            extensions: Default::default(),
616        }
617    }
618}
619
620macros::define_openapi_spec_object! {
621    pub struct SecurityScheme {
622        #[serde(flatten)]
623        pub r#type: SecuritySchemeType,
624        pub description: Option<String>,
625
626        #[serde(flatten)]
627        pub extensions: Extensions,
628    }
629}
630
631impl SecurityScheme {
632    pub fn new(r#type: SecuritySchemeType) -> Self {
633        Self {
634            r#type,
635            description: Default::default(),
636            extensions: Default::default(),
637        }
638    }
639}
640
641#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
642#[serde(rename_all = "camelCase", tag = "type")]
643#[non_exhaustive]
644#[expect(clippy::large_enum_variant)]
645pub enum SecuritySchemeType {
646    ApiKey(ApiKeySecurityScheme),
647    Http(HttpSecurityScheme),
648    MutualTLS,
649    Oauth2(Oauth2SecurityScheme),
650    OpenIdConnect(OpenIdConnectSecurityScheme),
651}
652
653#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
654#[serde(rename_all = "camelCase", tag = "in", content = "name")]
655#[non_exhaustive]
656pub enum ApiKeySecurityScheme {
657    Query(String),
658    Header(String),
659    Cookie(String),
660}
661
662macros::define_openapi_spec_object! {
663    pub struct HttpSecurityScheme {
664        pub scheme: String,
665        pub bearer_format: Option<String>,
666    }
667}
668
669impl HttpSecurityScheme {
670    pub fn new(scheme: String) -> Self {
671        Self {
672            scheme,
673            bearer_format: None,
674        }
675    }
676}
677
678macros::define_openapi_spec_object! {
679    #[derive(Default)]
680    pub struct Oauth2SecurityScheme {
681        pub flows: OAuthFlows,
682    }
683}
684
685macros::define_openapi_spec_object! {
686    pub struct OpenIdConnectSecurityScheme {
687        pub open_id_connect_url: String,
688    }
689}
690
691impl OpenIdConnectSecurityScheme {
692    pub fn new(open_id_connect_url: String) -> Self {
693        Self {
694            open_id_connect_url,
695        }
696    }
697}
698
699macros::define_openapi_spec_object! {
700    #[derive(Default)]
701    pub struct OAuthFlows {
702        pub implicit: Option<OAuthFlow>,
703        pub password: Option<OAuthFlow>,
704        pub client_credentials: Option<OAuthFlow>,
705        pub authorization_code: Option<OAuthFlow>,
706
707        #[serde(flatten)]
708        pub extensions: Extensions,
709    }
710}
711
712macros::define_openapi_spec_object! {
713    #[derive(Default)]
714    pub struct OAuthFlow {
715        pub authorization_url: Option<String>,
716        pub token_url: Option<String>,
717        pub refresh_url: Option<String>,
718        #[serde(default, skip_serializing_if = "Map::is_empty")]
719        pub scopes: Map<String, String>,
720
721        #[serde(flatten)]
722        pub extensions: Extensions,
723    }
724}
725
726macros::define_openapi_spec_object! {
727    #[derive(Default)]
728    pub struct SecurityRequirement {
729        #[serde(flatten)]
730        pub schemes: Map<String, Vec<String>>,
731    }
732}
733
734impl SecurityRequirement {
735    pub fn is_empty(&self) -> bool {
736        self.schemes.is_empty()
737    }
738}
739
740#[derive(Default, Clone, Debug, PartialEq, Eq)]
741pub struct Extensions(pub Map<String, Value>);
742
743impl Extensions {
744    pub fn merge_with(&mut self, other: Extensions) {
745        merge_maps(&mut self.0, other.0, |value_mut, value| {
746            if *value_mut != value {
747                panic!("colliding extension values")
748            }
749        });
750    }
751}
752
753impl Deref for Extensions {
754    type Target = Map<String, Value>;
755
756    fn deref(&self) -> &Self::Target {
757        &self.0
758    }
759}
760
761impl DerefMut for Extensions {
762    fn deref_mut(&mut self) -> &mut Self::Target {
763        &mut self.0
764    }
765}
766
767impl Serialize for Extensions {
768    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
769    where
770        S: serde::Serializer,
771    {
772        let mut map = serializer.serialize_map(Some(self.0.len()))?;
773        for (k, v) in &self.0 {
774            map.serialize_entry(&format!("x-{k}"), v)?;
775        }
776        map.end()
777    }
778}
779
780impl<'de> Deserialize<'de> for Extensions {
781    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
782    where
783        D: serde::Deserializer<'de>,
784    {
785        let map = Map::<String, Value>::deserialize(deserializer)?;
786        let map = map
787            .into_iter()
788            .filter(|(k, _)| k.starts_with("x-"))
789            .map(|(mut k, v)| {
790                k.replace_range(0..2, "");
791                (k, v)
792            })
793            .collect();
794        Ok(Self(map))
795    }
796}
797
798#[cfg(test)]
799mod tests {
800    use insta::{assert_debug_snapshot, assert_json_snapshot};
801    use serde_json::json;
802
803    use super::*;
804
805    #[test]
806    pub fn extensions() {
807        assert_json_snapshot!(Extensions(Map::from_iter([
808            (String::from("foo"), json!("value")),
809            (String::from("bar"), json!(42))
810        ])));
811        assert_debug_snapshot!(serde_json::from_value::<Extensions>(json!({
812            "x-foo": 42,
813            "bar": "nowhere",
814            "x-foobar": "ok",
815        })));
816    }
817
818    #[test]
819    pub fn defaults() {
820        assert_json_snapshot!(OpenApi::new(
821            "3.0.0".to_owned(),
822            Info::new("Title".to_owned())
823        ));
824        assert_json_snapshot!(Contact::default());
825        assert_json_snapshot!(License::new("GPL".to_owned()));
826        assert_json_snapshot!(Server::new("/url".to_owned()));
827        assert_json_snapshot!(Components::default());
828        assert_json_snapshot!(Paths::default());
829        assert_json_snapshot!(PathItem::default());
830        assert_json_snapshot!(Operation::default());
831        assert_json_snapshot!(ExternalDocs::new("/url".to_owned()));
832        assert_json_snapshot!(Parameter::new("name".to_owned(), ParameterIn::Cookie));
833        assert_json_snapshot!(RequestBody::new(Map::default()));
834        assert_json_snapshot!(MediaTypeContent::default());
835        assert_json_snapshot!(Encoding::default());
836        assert_json_snapshot!(Responses::default());
837        assert_json_snapshot!(Response::default());
838        assert_json_snapshot!(Callback::default());
839        assert_json_snapshot!(Example::default());
840        assert_json_snapshot!(Link::default());
841        assert_json_snapshot!(Header::default());
842        assert_json_snapshot!(Tag::new("name".to_owned()));
843        assert_json_snapshot!(Ref::new("ref".to_owned()));
844        assert_json_snapshot!(SecurityScheme::new(SecuritySchemeType::MutualTLS));
845        assert_json_snapshot!(SecurityScheme::new(SecuritySchemeType::ApiKey(
846            ApiKeySecurityScheme::Cookie("cookie_name".to_owned())
847        )));
848        assert_json_snapshot!(SecurityScheme::new(SecuritySchemeType::Oauth2(
849            Oauth2SecurityScheme::default()
850        )));
851        assert_json_snapshot!(SecurityScheme::new(SecuritySchemeType::Oauth2(
852            Oauth2SecurityScheme::default()
853                .with_flows(OAuthFlows::default().with_implicit(OAuthFlow::default()))
854        )));
855        assert_json_snapshot!(SecurityScheme::new(SecuritySchemeType::OpenIdConnect(
856            OpenIdConnectSecurityScheme::new("/url".to_owned())
857        )));
858        assert_json_snapshot!(SecurityScheme::new(SecuritySchemeType::Http(
859            HttpSecurityScheme::new("scheme".to_owned())
860        )));
861        assert_json_snapshot!(OAuthFlows::default());
862        assert_json_snapshot!(OAuthFlow::default());
863        assert_json_snapshot!(SecurityRequirement::default());
864    }
865}