Skip to main content

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