salvo_oapi/openapi/components.rs
1//! Implements [OpenAPI Schema Object][schema] types which can be
2//! used to define field properties, enum values, array or object types.
3//!
4//! [schema]: https://spec.openapis.org/oas/latest.html#schema-object
5use serde::{Deserialize, Serialize};
6
7use crate::{PropMap, RefOr, Response, Responses, Schema, Schemas, SecurityScheme};
8
9/// Implements [OpenAPI Components Object][components] which holds supported
10/// reusable objects.
11///
12/// Components can hold either reusable types themselves or references to other reusable
13/// types.
14///
15/// [components]: https://spec.openapis.org/oas/latest.html#components-object
16#[non_exhaustive]
17#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
18#[serde(rename_all = "camelCase")]
19pub struct Components {
20 /// Map of reusable [OpenAPI Schema Object][schema]s.
21 ///
22 /// [schema]: https://spec.openapis.org/oas/latest.html#schema-object
23 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
24 pub schemas: Schemas,
25
26 /// Map of reusable response name, to [OpenAPI Response Object][response]s or [OpenAPI
27 /// Reference][reference]s to [OpenAPI Response Object][response]s.
28 ///
29 /// [response]: https://spec.openapis.org/oas/latest.html#response-object
30 /// [reference]: https://spec.openapis.org/oas/latest.html#reference-object
31 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
32 pub responses: Responses,
33
34 /// Map of reusable [OpenAPI Security Scheme Object][security_scheme]s.
35 ///
36 /// [security_scheme]: https://spec.openapis.org/oas/latest.html#security-scheme-object
37 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
38 pub security_schemes: PropMap<String, SecurityScheme>,
39}
40
41impl Components {
42 /// Construct a new empty [`Components`]. This is effectively same as calling [`Components::default`].
43 pub fn new() -> Self {
44 Default::default()
45 }
46
47 /// Add [`SecurityScheme`] to [`Components`] and returns `Self`.
48 ///
49 /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
50 /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
51 ///
52 /// [requirement]: crate::SecurityRequirement
53 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
54 mut self,
55 name: N,
56 security_scheme: S,
57 ) -> Self {
58 self.security_schemes
59 .insert(name.into(), security_scheme.into());
60
61 self
62 }
63
64 /// Add iterator of [`SecurityScheme`]s to [`Components`].
65 ///
66 /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
67 /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
68 ///
69 /// [requirement]: crate::SecurityRequirement
70 pub fn extend_security_schemes<
71 I: IntoIterator<Item = (N, S)>,
72 N: Into<String>,
73 S: Into<SecurityScheme>,
74 >(
75 mut self,
76 schemas: I,
77 ) -> Self {
78 self.security_schemes.extend(
79 schemas
80 .into_iter()
81 .map(|(name, item)| (name.into(), item.into())),
82 );
83 self
84 }
85
86 /// Add [`Schema`] to [`Components`] and returns `Self`.
87 ///
88 /// Accepts two arguments where first is name of the schema and second is the schema itself.
89 pub fn add_schema<S: Into<String>, I: Into<RefOr<Schema>>>(
90 mut self,
91 name: S,
92 schema: I,
93 ) -> Self {
94 self.schemas.insert(name, schema);
95 self
96 }
97
98 /// Add [`Schema`]s from iterator.
99 ///
100 /// # Examples
101 /// ```
102 /// # use salvo_oapi::{Components, Object, BasicType, Schema};
103 /// Components::new().extend_schemas([(
104 /// "Pet",
105 /// Schema::from(
106 /// Object::new()
107 /// .property(
108 /// "name",
109 /// Object::new().schema_type(BasicType::String),
110 /// )
111 /// .required("name")
112 /// ),
113 /// )]);
114 /// ```
115 pub fn extend_schemas<I, C, S>(mut self, schemas: I) -> Self
116 where
117 I: IntoIterator<Item = (S, C)>,
118 C: Into<RefOr<Schema>>,
119 S: Into<String>,
120 {
121 self.schemas.extend(
122 schemas
123 .into_iter()
124 .map(|(name, schema)| (name.into(), schema.into())),
125 );
126 self
127 }
128
129 /// Add a new response and returns `self`.
130 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
131 mut self,
132 name: S,
133 response: R,
134 ) -> Self {
135 self.responses.insert(name.into(), response.into());
136 self
137 }
138
139 /// Extends responses with the contents of an iterator.
140 pub fn extend_responses<
141 I: IntoIterator<Item = (S, R)>,
142 S: Into<String>,
143 R: Into<RefOr<Response>>,
144 >(
145 mut self,
146 responses: I,
147 ) -> Self {
148 self.responses.extend(
149 responses
150 .into_iter()
151 .map(|(name, response)| (name.into(), response.into())),
152 );
153 self
154 }
155
156 /// Moves all elements from `other` into `self`, leaving `other` empty.
157 ///
158 /// If a key from `other` is already present in `self`, the respective
159 /// value from `self` will be overwritten with the respective value from `other`.
160 pub fn append(&mut self, other: &mut Components) {
161 other
162 .schemas
163 .retain(|name, _| !self.schemas.contains_key(name));
164 self.schemas.append(&mut other.schemas);
165
166 other
167 .responses
168 .retain(|name, _| !self.responses.contains_key(name));
169 self.responses.append(&mut other.responses);
170
171 other
172 .security_schemes
173 .retain(|name, _| !self.security_schemes.contains_key(name));
174 self.security_schemes.append(&mut other.security_schemes);
175 }
176
177 /// Returns `true` if instance contains no elements.
178 pub fn is_empty(&self) -> bool {
179 self.schemas.is_empty() && self.responses.is_empty() && self.security_schemes.is_empty()
180 }
181}