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]
159 pub fn default_value(mut self, default: Value) -> Self {
160 self.default_value = Some(default);
161 self
162 }
163
164 #[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 #[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 #[must_use]
180 pub fn unique_items(mut self, unique_items: bool) -> Self {
181 self.unique_items = unique_items;
182 self
183 }
184
185 #[must_use]
187 pub fn xml(mut self, xml: Xml) -> Self {
188 self.xml = Some(xml);
189 self
190 }
191
192 #[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#[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}