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
43 /// [`Components::default`].
44 #[must_use]
45 pub fn new() -> Self {
46 Default::default()
47 }
48
49 /// Add [`SecurityScheme`] to [`Components`] and returns `Self`.
50 ///
51 /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
52 /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the
53 /// [`SecurityScheme`].
54 ///
55 /// [requirement]: crate::SecurityRequirement
56 #[must_use]
57 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
58 mut self,
59 name: N,
60 security_scheme: S,
61 ) -> Self {
62 self.security_schemes
63 .insert(name.into(), security_scheme.into());
64
65 self
66 }
67
68 /// Add iterator of [`SecurityScheme`]s to [`Components`].
69 ///
70 /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
71 /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the
72 /// [`SecurityScheme`].
73 ///
74 /// [requirement]: crate::SecurityRequirement
75 #[must_use]
76 pub fn extend_security_schemes<
77 I: IntoIterator<Item = (N, S)>,
78 N: Into<String>,
79 S: Into<SecurityScheme>,
80 >(
81 mut self,
82 schemas: I,
83 ) -> Self {
84 self.security_schemes.extend(
85 schemas
86 .into_iter()
87 .map(|(name, item)| (name.into(), item.into())),
88 );
89 self
90 }
91
92 /// Add [`Schema`] to [`Components`] and returns `Self`.
93 ///
94 /// Accepts two arguments where first is name of the schema and second is the schema itself.
95 #[must_use]
96 pub fn add_schema<S: Into<String>, I: Into<RefOr<Schema>>>(
97 mut self,
98 name: S,
99 schema: I,
100 ) -> Self {
101 self.schemas.insert(name, schema);
102 self
103 }
104
105 /// Add [`Schema`]s from iterator.
106 ///
107 /// # Examples
108 /// ```
109 /// # use salvo_oapi::{Components, Object, BasicType, Schema};
110 /// Components::new().extend_schemas([(
111 /// "Pet",
112 /// Schema::from(
113 /// Object::new()
114 /// .property("name", Object::new().schema_type(BasicType::String))
115 /// .required("name"),
116 /// ),
117 /// )]);
118 /// ```
119 #[must_use]
120 pub fn extend_schemas<I, C, S>(mut self, schemas: I) -> Self
121 where
122 I: IntoIterator<Item = (S, C)>,
123 C: Into<RefOr<Schema>>,
124 S: Into<String>,
125 {
126 self.schemas.extend(
127 schemas
128 .into_iter()
129 .map(|(name, schema)| (name.into(), schema.into())),
130 );
131 self
132 }
133
134 /// Add a new response and returns `self`.
135 #[must_use]
136 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
137 mut self,
138 name: S,
139 response: R,
140 ) -> Self {
141 self.responses.insert(name.into(), response.into());
142 self
143 }
144
145 /// Extends responses with the contents of an iterator.
146 #[must_use]
147 pub fn extend_responses<
148 I: IntoIterator<Item = (S, R)>,
149 S: Into<String>,
150 R: Into<RefOr<Response>>,
151 >(
152 mut self,
153 responses: I,
154 ) -> Self {
155 self.responses.extend(
156 responses
157 .into_iter()
158 .map(|(name, response)| (name.into(), response.into())),
159 );
160 self
161 }
162
163 /// Moves all elements from `other` into `self`, leaving `other` empty.
164 ///
165 /// If a key from `other` is already present in `self`, the respective
166 /// value from `self` will be overwritten with the respective value from `other`.
167 pub fn append(&mut self, other: &mut Self) {
168 other
169 .schemas
170 .retain(|name, _| !self.schemas.contains_key(name));
171 self.schemas.append(&mut other.schemas);
172
173 other
174 .responses
175 .retain(|name, _| !self.responses.contains_key(name));
176 self.responses.append(&mut other.responses);
177
178 other
179 .security_schemes
180 .retain(|name, _| !self.security_schemes.contains_key(name));
181 self.security_schemes.append(&mut other.security_schemes);
182 }
183
184 /// Returns `true` if instance contains no elements.
185 #[must_use]
186 pub fn is_empty(&self) -> bool {
187 self.schemas.is_empty() && self.responses.is_empty() && self.security_schemes.is_empty()
188 }
189}