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 #[must_use]
44 pub fn new() -> Self {
45 Default::default()
46 }
47
48 /// Add [`SecurityScheme`] to [`Components`] and returns `Self`.
49 ///
50 /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
51 /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
52 ///
53 /// [requirement]: crate::SecurityRequirement
54 #[must_use]
55 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
56 mut self,
57 name: N,
58 security_scheme: S,
59 ) -> Self {
60 self.security_schemes
61 .insert(name.into(), security_scheme.into());
62
63 self
64 }
65
66 /// Add iterator of [`SecurityScheme`]s to [`Components`].
67 ///
68 /// Accepts two arguments where first is the name of the [`SecurityScheme`]. This is later when
69 /// referenced by [`SecurityRequirement`][requirement]s. Second parameter is the [`SecurityScheme`].
70 ///
71 /// [requirement]: crate::SecurityRequirement
72 #[must_use]
73 pub fn extend_security_schemes<
74 I: IntoIterator<Item = (N, S)>,
75 N: Into<String>,
76 S: Into<SecurityScheme>,
77 >(
78 mut self,
79 schemas: I,
80 ) -> Self {
81 self.security_schemes.extend(
82 schemas
83 .into_iter()
84 .map(|(name, item)| (name.into(), item.into())),
85 );
86 self
87 }
88
89 /// Add [`Schema`] to [`Components`] and returns `Self`.
90 ///
91 /// Accepts two arguments where first is name of the schema and second is the schema itself.
92 #[must_use]
93 pub fn add_schema<S: Into<String>, I: Into<RefOr<Schema>>>(
94 mut self,
95 name: S,
96 schema: I,
97 ) -> Self {
98 self.schemas.insert(name, schema);
99 self
100 }
101
102 /// Add [`Schema`]s from iterator.
103 ///
104 /// # Examples
105 /// ```
106 /// # use salvo_oapi::{Components, Object, BasicType, Schema};
107 /// Components::new().extend_schemas([(
108 /// "Pet",
109 /// Schema::from(
110 /// Object::new()
111 /// .property(
112 /// "name",
113 /// Object::new().schema_type(BasicType::String),
114 /// )
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}