salvo_oapi/openapi/schema/
array.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::schema::BasicType;
5use crate::{Deprecated, PropMap, RefOr, Schema, SchemaType, Xml};
6
7#[non_exhaustive]
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
12#[serde(rename_all = "camelCase")]
13pub struct Array {
14 #[serde(rename = "type")]
16 pub schema_type: SchemaType,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub title: Option<String>,
21
22 pub items: Box<RefOr<Schema>>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub description: Option<String>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub deprecated: Option<Deprecated>,
32
33 #[serde(skip_serializing_if = "Vec::is_empty", default)]
35 pub examples: Vec<Value>,
36
37 #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
39 pub default_value: Option<Value>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub max_items: Option<usize>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub min_items: Option<usize>,
48
49 #[serde(default, skip_serializing_if = "super::is_false")]
52 pub unique_items: bool,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub xml: Option<Xml>,
57
58 #[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 #[must_use]
93 pub fn new() -> Self {
94 Self::default()
95 }
96 #[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 #[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 #[must_use]
123 pub fn title(mut self, title: impl Into<String>) -> Self {
124 self.title = Some(title.into());
125 self
126 }
127
128 #[must_use]
130 pub fn description(mut self, description: impl Into<String>) -> Self {
131 self.description = Some(description.into());
132 self
133 }
134
135 #[must_use]
137 pub fn deprecated(mut self, deprecated: Deprecated) -> Self {
138 self.deprecated = Some(deprecated);
139 self
140 }
141
142 #[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 #[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 #[must_use]
158 pub fn default_value(mut self, default: Value) -> Self {
159 self.default_value = Some(default);
160 self
161 }
162
163 #[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 #[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 #[must_use]
179 pub fn unique_items(mut self, unique_items: bool) -> Self {
180 self.unique_items = unique_items;
181 self
182 }
183
184 #[must_use]
186 pub fn xml(mut self, xml: Xml) -> Self {
187 self.xml = Some(xml);
188 self
189 }
190
191 #[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#[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}