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}