salvo_oapi/openapi/schema/
array.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::schema::BasicType;
5use crate::{Deprecated, PropMap, RefOr, Schema, SchemaType, Xml};
6
7/// Array represents [`Vec`] or [`slice`] type  of items.
8///
9/// See [`Schema::Array`] for more details.
10#[non_exhaustive]
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
12#[serde(rename_all = "camelCase")]
13pub struct Array {
14    /// Type will always be [`SchemaType::Array`]
15    #[serde(rename = "type")]
16    pub schema_type: SchemaType,
17
18    /// Changes the [`Array`] title.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub title: Option<String>,
21
22    /// Schema representing the array items type.
23    pub items: Box<RefOr<Schema>>,
24
25    /// Description of the [`Array`]. Markdown syntax is supported.
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub description: Option<String>,
28
29    /// Marks the [`Array`] deprecated.
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub deprecated: Option<Deprecated>,
32
33    /// Examples shown in UI of the value for richer documentation.
34    #[serde(skip_serializing_if = "Vec::is_empty", default)]
35    pub examples: Vec<Value>,
36
37    /// Default value which is provided when user has not provided the input in Swagger UI.
38    #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
39    pub default_value: Option<Value>,
40
41    /// Max length of the array.
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub max_items: Option<usize>,
44
45    /// Min length of the array.
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub min_items: Option<usize>,
48
49    /// Setting this to `true` will validate successfully if all elements of this [`Array`] are
50    /// unique.
51    #[serde(default, skip_serializing_if = "super::is_false")]
52    pub unique_items: bool,
53
54    /// Xml format of the array.
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub xml: Option<Xml>,
57
58    /// Optional extensions `x-something`.
59    #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
60    pub extensions: PropMap<String, serde_json::Value>,
61}
62
63impl Default for Array {
64    fn default() -> Self {
65        Self {
66            title: Default::default(),
67            schema_type: BasicType::Array.into(),
68            unique_items: bool::default(),
69            items: Default::default(),
70            description: Default::default(),
71            deprecated: Default::default(),
72            examples: Default::default(),
73            default_value: Default::default(),
74            max_items: Default::default(),
75            min_items: Default::default(),
76            xml: Default::default(),
77            extensions: Default::default(),
78        }
79    }
80}
81
82impl Array {
83    /// Construct a new [`Array`] component from given [`Schema`].
84    ///
85    /// # Examples
86    ///
87    /// _**Create a `String` array component.**_
88    /// ```
89    /// # use salvo_oapi::schema::{Schema, Array, SchemaType, BasicType, Object};
90    /// let string_array = Array::new().items(Object::with_type(BasicType::String));
91    /// ```
92    #[must_use]
93    pub fn new() -> Self {
94        Self::default()
95    }
96    /// Set [`Schema`] type for the [`Array`].
97    #[must_use]
98    pub fn items<I: Into<RefOr<Schema>>>(mut self, items: I) -> Self {
99        self.items = Box::new(items.into());
100        self
101    }
102
103    /// Change type of the array e.g. to change type to _`string`_
104    /// use value `SchemaType::Type(Type::String)`.
105    ///
106    /// # Examples
107    ///
108    /// _**Make nullable string array.**_
109    /// ```rust
110    /// # use salvo_oapi::schema::{Array, BasicType, SchemaType, Object};
111    /// let _ = Array::new()
112    ///     .schema_type(SchemaType::from_iter([BasicType::Array, BasicType::Null]))
113    ///     .items(Object::with_type(BasicType::String));
114    /// ```
115    #[must_use]
116    pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
117        self.schema_type = schema_type.into();
118        self
119    }
120
121    /// Add or change the title of the [`Array`].
122    #[must_use]
123    pub fn title(mut self, title: impl Into<String>) -> Self {
124        self.title = Some(title.into());
125        self
126    }
127
128    /// Add or change description of the property. Markdown syntax is supported.
129    #[must_use]
130    pub fn description(mut self, description: impl Into<String>) -> Self {
131        self.description = Some(description.into());
132        self
133    }
134
135    /// Add or change deprecated status for [`Array`].
136    #[must_use]
137    pub fn deprecated(mut self, deprecated: Deprecated) -> Self {
138        self.deprecated = Some(deprecated);
139        self
140    }
141
142    /// Add or change example shown in UI of the value for richer documentation.
143    #[must_use]
144    pub fn example<V: Into<Value>>(mut self, example: V) -> Self {
145        self.examples.push(example.into());
146        self
147    }
148
149    /// Add or change example shown in UI of the value for richer documentation.
150    #[must_use]
151    pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
152        self.examples = examples.into_iter().map(Into::into).collect();
153        self
154    }
155
156    /// Add or change default value for the object which is provided when user has not provided the
157    /// input in Swagger UI.
158    #[must_use]
159    pub fn default_value(mut self, default: Value) -> Self {
160        self.default_value = Some(default);
161        self
162    }
163
164    /// Set maximum allowed length for [`Array`].
165    #[must_use]
166    pub fn max_items(mut self, max_items: usize) -> Self {
167        self.max_items = Some(max_items);
168        self
169    }
170
171    /// Set minimum allowed length for [`Array`].
172    #[must_use]
173    pub fn min_items(mut self, min_items: usize) -> Self {
174        self.min_items = Some(min_items);
175        self
176    }
177
178    /// Set or change whether [`Array`] should enforce all items to be unique.
179    #[must_use]
180    pub fn unique_items(mut self, unique_items: bool) -> Self {
181        self.unique_items = unique_items;
182        self
183    }
184
185    /// Set [`Xml`] formatting for [`Array`].
186    #[must_use]
187    pub fn xml(mut self, xml: Xml) -> Self {
188        self.xml = Some(xml);
189        self
190    }
191
192    /// Add openapi extension (`x-something`) for [`Array`].
193    #[must_use]
194    pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
195        self.extensions.insert(key.into(), value);
196        self
197    }
198}
199
200impl From<Array> for Schema {
201    fn from(array: Array) -> Self {
202        Self::Array(array)
203    }
204}
205
206impl From<Array> for RefOr<Schema> {
207    fn from(array: Array) -> Self {
208        Self::Type(Schema::Array(array))
209    }
210}
211
212// impl ToArray for Array {}
213
214// /// Trait for converting a type to [`Array`].
215// pub trait ToArray
216// where
217//     RefOr<Schema>: From<Self>,
218//     Self: Sized,
219// {
220//     /// Convert a type to [`Array`].
221//     pub fn to_array(self) -> Array {
222//         Array::new().items(self)
223//     }
224// }
225
226#[cfg(test)]
227mod tests {
228    use assert_json_diff::assert_json_eq;
229    use serde_json::json;
230
231    use super::*;
232    use crate::Object;
233
234    #[test]
235    fn test_build_array() {
236        let array = Array::new()
237            .items(Object::with_type(BasicType::String))
238            .title("title")
239            .description("description")
240            .deprecated(Deprecated::False)
241            .examples([
242                Value::String("example1".to_owned()),
243                Value::String("example2".to_owned()),
244            ])
245            .default_value(Value::String("default".to_owned()))
246            .max_items(10)
247            .min_items(1)
248            .unique_items(true)
249            .xml(Xml::new());
250
251        assert_json_eq!(
252            array,
253            json!({
254                "type": "array",
255                "items": {
256                    "type": "string"
257                },
258                "title": "title",
259                "description": "description",
260                "deprecated": false,
261                "examples": ["example1", "example2"],
262                "default": "default",
263                "maxItems": 10,
264                "minItems": 1,
265                "uniqueItems": true,
266                "xml": {},
267            })
268        )
269    }
270
271    #[test]
272    fn test_schema_from_array() {
273        let array = Array::default();
274        let schema = Schema::from(array);
275        assert_json_eq!(
276            schema,
277            json!({
278                "type": "array",
279                "items": {
280                    "type": "object"
281                }
282            })
283        )
284    }
285
286    #[test]
287    fn test_array_with_extensions() {
288        let expected = json!("value");
289        let json_value = Array::default().add_extension("x-some-extension", expected.clone());
290
291        let value = serde_json::to_value(&json_value).unwrap();
292        assert_eq!(value.get("x-some-extension"), Some(&expected));
293    }
294}