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 input in Swagger UI.
157    #[must_use]
158    pub fn default_value(mut self, default: Value) -> Self {
159        self.default_value = Some(default);
160        self
161    }
162
163    /// Set maximum allowed length for [`Array`].
164    #[must_use]
165    pub fn max_items(mut self, max_items: usize) -> Self {
166        self.max_items = Some(max_items);
167        self
168    }
169
170    /// Set minimum allowed length for [`Array`].
171    #[must_use]
172    pub fn min_items(mut self, min_items: usize) -> Self {
173        self.min_items = Some(min_items);
174        self
175    }
176
177    /// Set or change whether [`Array`] should enforce all items to be unique.
178    #[must_use]
179    pub fn unique_items(mut self, unique_items: bool) -> Self {
180        self.unique_items = unique_items;
181        self
182    }
183
184    /// Set [`Xml`] formatting for [`Array`].
185    #[must_use]
186    pub fn xml(mut self, xml: Xml) -> Self {
187        self.xml = Some(xml);
188        self
189    }
190
191    /// Add openapi extension (`x-something`) for [`Array`].
192    #[must_use]
193    pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
194        self.extensions.insert(key.into(), value);
195        self
196    }
197}
198
199impl From<Array> for Schema {
200    fn from(array: Array) -> Self {
201        Self::Array(array)
202    }
203}
204
205impl From<Array> for RefOr<Schema> {
206    fn from(array: Array) -> Self {
207        Self::Type(Schema::Array(array))
208    }
209}
210
211// impl ToArray for Array {}
212
213// /// Trait for converting a type to [`Array`].
214// pub trait ToArray
215// where
216//     RefOr<Schema>: From<Self>,
217//     Self: Sized,
218// {
219//     /// Convert a type to [`Array`].
220//     pub fn to_array(self) -> Array {
221//         Array::new().items(self)
222//     }
223// }
224
225#[cfg(test)]
226mod tests {
227    use assert_json_diff::assert_json_eq;
228    use serde_json::json;
229
230    use super::*;
231    use crate::Object;
232
233    #[test]
234    fn test_build_array() {
235        let array = Array::new()
236            .items(Object::with_type(BasicType::String))
237            .title("title")
238            .description("description")
239            .deprecated(Deprecated::False)
240            .examples([
241                Value::String("example1".to_owned()),
242                Value::String("example2".to_owned()),
243            ])
244            .default_value(Value::String("default".to_owned()))
245            .max_items(10)
246            .min_items(1)
247            .unique_items(true)
248            .xml(Xml::new());
249
250        assert_json_eq!(
251            array,
252            json!({
253                "type": "array",
254                "items": {
255                    "type": "string"
256                },
257                "title": "title",
258                "description": "description",
259                "deprecated": false,
260                "examples": ["example1", "example2"],
261                "default": "default",
262                "maxItems": 10,
263                "minItems": 1,
264                "uniqueItems": true,
265                "xml": {},
266            })
267        )
268    }
269
270    #[test]
271    fn test_schema_from_array() {
272        let array = Array::default();
273        let schema = Schema::from(array);
274        assert_json_eq!(
275            schema,
276            json!({
277                "type": "array",
278                "items": {
279                    "type": "object"
280                }
281            })
282        )
283    }
284
285    #[test]
286    fn test_array_with_extensions() {
287        let expected = json!("value");
288        let json_value = Array::default().add_extension("x-some-extension", expected.clone());
289
290        let value = serde_json::to_value(&json_value).unwrap();
291        assert_eq!(value.get("x-some-extension"), Some(&expected));
292    }
293}