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 pub fn new() -> Self {
93 Self::default()
94 }
95 pub fn items<I: Into<RefOr<Schema>>>(mut self, items: I) -> Self {
97 self.items = Box::new(items.into());
98 self
99 }
100
101 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
114 self.schema_type = schema_type.into();
115 self
116 }
117
118 pub fn title(mut self, title: impl Into<String>) -> Self {
120 self.title = Some(title.into());
121 self
122 }
123
124 pub fn description(mut self, description: impl Into<String>) -> Self {
126 self.description = Some(description.into());
127 self
128 }
129
130 pub fn deprecated(mut self, deprecated: Deprecated) -> Self {
132 self.deprecated = Some(deprecated);
133 self
134 }
135
136 pub fn example<V: Into<Value>>(mut self, example: V) -> Self {
138 self.examples.push(example.into());
139 self
140 }
141
142 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
144 self.examples = examples.into_iter().map(Into::into).collect();
145 self
146 }
147
148 pub fn default_value(mut self, default: Value) -> Self {
150 self.default_value = Some(default);
151 self
152 }
153
154 pub fn max_items(mut self, max_items: usize) -> Self {
156 self.max_items = Some(max_items);
157 self
158 }
159
160 pub fn min_items(mut self, min_items: usize) -> Self {
162 self.min_items = Some(min_items);
163 self
164 }
165
166 pub fn unique_items(mut self, unique_items: bool) -> Self {
168 self.unique_items = unique_items;
169 self
170 }
171
172 pub fn xml(mut self, xml: Xml) -> Self {
174 self.xml = Some(xml);
175 self
176 }
177
178 pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
180 self.extensions.insert(key.into(), value);
181 self
182 }
183}
184
185impl From<Array> for Schema {
186 fn from(array: Array) -> Self {
187 Self::Array(array)
188 }
189}
190
191impl From<Array> for RefOr<Schema> {
192 fn from(array: Array) -> Self {
193 Self::Type(Schema::Array(array))
194 }
195}
196
197impl ToArray for Array {}
198
199pub trait ToArray
201where
202 RefOr<Schema>: From<Self>,
203 Self: Sized,
204{
205 fn to_array(self) -> Array {
207 Array::new().items(self)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use assert_json_diff::assert_json_eq;
214 use serde_json::json;
215
216 use super::*;
217 use crate::Object;
218
219 #[test]
220 fn test_build_array() {
221 let array = Array::new()
222 .items(Object::with_type(BasicType::String))
223 .title("title")
224 .description("description")
225 .deprecated(Deprecated::False)
226 .examples([
227 Value::String("example1".to_string()),
228 Value::String("example2".to_string()),
229 ])
230 .default_value(Value::String("default".to_string()))
231 .max_items(10)
232 .min_items(1)
233 .unique_items(true)
234 .xml(Xml::new());
235
236 assert_json_eq!(
237 array,
238 json!({
239 "type": "array",
240 "items": {
241 "type": "string"
242 },
243 "title": "title",
244 "description": "description",
245 "deprecated": false,
246 "examples": ["example1", "example2"],
247 "default": "default",
248 "maxItems": 10,
249 "minItems": 1,
250 "uniqueItems": true,
251 "xml": {},
252 })
253 )
254 }
255
256 #[test]
257 fn test_schema_from_array() {
258 let array = Array::default();
259 let schema = Schema::from(array);
260 assert_json_eq!(
261 schema,
262 json!({
263 "type": "array",
264 "items": {
265 "type": "object"
266 }
267 })
268 )
269 }
270
271 #[test]
272 fn test_array_with_extensions() {
273 let expected = json!("value");
274 let json_value = Array::default().add_extension("x-some-extension", expected.clone());
275
276 let value = serde_json::to_value(&json_value).unwrap();
277 assert_eq!(value.get("x-some-extension"), Some(&expected));
278 }
279}