salvo_oapi/openapi/schema/
object.rs1use indexmap::IndexSet;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5use super::AdditionalProperties;
6use crate::{Deprecated, PropMap, RefOr, Schema, SchemaFormat, SchemaType, ToArray, Xml};
7
8#[non_exhaustive]
15#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
16#[serde(rename_all = "camelCase")]
17pub struct Object {
18 #[serde(rename = "type", skip_serializing_if = "SchemaType::is_any_value")]
21 pub schema_type: SchemaType,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub name: Option<String>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub format: Option<SchemaFormat>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub description: Option<String>,
34
35 #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
37 pub default_value: Option<Value>,
38
39 #[serde(default, rename = "enum", skip_serializing_if = "Vec::is_empty")]
41 pub enum_values: Vec<Value>,
42
43 #[serde(default, skip_serializing_if = "IndexSet::is_empty")]
45 pub required: IndexSet<String>,
46
47 #[serde(default, skip_serializing_if = "PropMap::is_empty")]
55 pub properties: PropMap<String, RefOr<Schema>>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub additional_properties: Option<Box<AdditionalProperties<Schema>>>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub deprecated: Option<Deprecated>,
64
65 #[serde(skip_serializing_if = "Vec::is_empty", default)]
67 pub examples: Vec<Value>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub write_only: Option<bool>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub read_only: Option<bool>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub xml: Option<Xml>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
84 pub multiple_of: Option<f64>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
89 pub maximum: Option<f64>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
94 pub minimum: Option<f64>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
99 pub exclusive_maximum: Option<f64>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
104 pub exclusive_minimum: Option<f64>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
109 pub max_length: Option<usize>,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
115 pub min_length: Option<usize>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
120 pub pattern: Option<String>,
121
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub max_properties: Option<usize>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
129 pub min_properties: Option<usize>,
130
131 #[serde(default, skip_serializing_if = "PropMap::is_empty", flatten)]
133 pub extensions: PropMap<String, serde_json::Value>,
134
135 #[serde(skip_serializing_if = "String::is_empty", default)]
144 pub content_encoding: String,
145
146 #[serde(skip_serializing_if = "String::is_empty", default)]
151 pub content_media_type: String,
152}
153
154impl Object {
155 pub fn new() -> Self {
158 Default::default()
159 }
160
161 pub fn with_type<T: Into<SchemaType>>(schema_type: T) -> Self {
169 Self {
170 schema_type: schema_type.into(),
171 ..Default::default()
172 }
173 }
174
175 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
178 self.schema_type = schema_type.into();
179 self
180 }
181
182 pub fn format(mut self, format: SchemaFormat) -> Self {
184 self.format = Some(format);
185 self
186 }
187
188 pub fn property<S: Into<String>, I: Into<RefOr<Schema>>>(
192 mut self,
193 property_name: S,
194 component: I,
195 ) -> Self {
196 self.properties
197 .insert(property_name.into(), component.into());
198
199 self
200 }
201
202 pub fn additional_properties<I: Into<AdditionalProperties<Schema>>>(
204 mut self,
205 additional_properties: I,
206 ) -> Self {
207 self.additional_properties = Some(Box::new(additional_properties.into()));
208 self
209 }
210
211 pub fn required(mut self, required_field: impl Into<String>) -> Self {
213 self.required.insert(required_field.into());
214 self
215 }
216
217 pub fn name(mut self, name: impl Into<String>) -> Self {
219 self.name = Some(name.into());
220 self
221 }
222
223 pub fn description(mut self, description: impl Into<String>) -> Self {
225 self.description = Some(description.into());
226 self
227 }
228
229 pub fn default_value(mut self, default: Value) -> Self {
231 self.default_value = Some(default);
232 self
233 }
234
235 pub fn deprecated(mut self, deprecated: Deprecated) -> Self {
237 self.deprecated = Some(deprecated);
238 self
239 }
240
241 pub fn enum_values<I, E>(mut self, enum_values: I) -> Self
243 where
244 I: IntoIterator<Item = E>,
245 E: Into<Value>,
246 {
247 self.enum_values = enum_values
248 .into_iter()
249 .map(|enum_value| enum_value.into())
250 .collect();
251 self
252 }
253
254 pub fn example<V: Into<Value>>(mut self, example: V) -> Self {
256 self.examples.push(example.into());
257 self
258 }
259
260 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
262 self.examples = examples.into_iter().map(Into::into).collect();
263 self
264 }
265
266 pub fn write_only(mut self, write_only: bool) -> Self {
268 self.write_only = Some(write_only);
269 self
270 }
271
272 pub fn read_only(mut self, read_only: bool) -> Self {
274 self.read_only = Some(read_only);
275 self
276 }
277
278 pub fn xml(mut self, xml: Xml) -> Self {
280 self.xml = Some(xml);
281 self
282 }
283
284 pub fn multiple_of(mut self, multiple_of: f64) -> Self {
286 self.multiple_of = Some(multiple_of);
287 self
288 }
289
290 pub fn maximum(mut self, maximum: f64) -> Self {
292 self.maximum = Some(maximum);
293 self
294 }
295
296 pub fn minimum(mut self, minimum: f64) -> Self {
298 self.minimum = Some(minimum);
299 self
300 }
301
302 pub fn exclusive_maximum(mut self, exclusive_maximum: f64) -> Self {
304 self.exclusive_maximum = Some(exclusive_maximum);
305 self
306 }
307
308 pub fn exclusive_minimum(mut self, exclusive_minimum: f64) -> Self {
310 self.exclusive_minimum = Some(exclusive_minimum);
311 self
312 }
313
314 pub fn max_length(mut self, max_length: usize) -> Self {
316 self.max_length = Some(max_length);
317 self
318 }
319
320 pub fn min_length(mut self, min_length: usize) -> Self {
322 self.min_length = Some(min_length);
323 self
324 }
325
326 pub fn pattern<I: Into<String>>(mut self, pattern: I) -> Self {
328 self.pattern = Some(pattern.into());
329 self
330 }
331
332 pub fn max_properties(mut self, max_properties: usize) -> Self {
334 self.max_properties = Some(max_properties);
335 self
336 }
337
338 pub fn min_properties(mut self, min_properties: usize) -> Self {
340 self.min_properties = Some(min_properties);
341 self
342 }
343
344 pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
346 self.extensions.insert(key.into(), value);
347 self
348 }
349
350 pub fn content_encoding<S: Into<String>>(mut self, content_encoding: S) -> Self {
353 self.content_encoding = content_encoding.into();
354 self
355 }
356
357 pub fn content_media_type<S: Into<String>>(mut self, content_media_type: S) -> Self {
360 self.content_media_type = content_media_type.into();
361 self
362 }
363}
364
365impl From<Object> for Schema {
366 fn from(s: Object) -> Self {
367 Self::Object(Box::new(s))
368 }
369}
370
371impl ToArray for Object {}
372
373impl From<Object> for RefOr<Schema> {
374 fn from(obj: Object) -> Self {
375 Self::Type(Schema::Object(Box::new(obj)))
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use assert_json_diff::assert_json_eq;
382 use serde_json::json;
383
384 use super::*;
385 use crate::BasicType;
386
387 #[test]
388 fn test_build_string_object() {
389 let object = Object::new()
390 .schema_type(BasicType::String)
391 .deprecated(Deprecated::True)
392 .write_only(false)
393 .read_only(true)
394 .xml(Xml::new())
395 .max_length(10)
396 .min_length(1)
397 .pattern(r"^[a-z]+$");
398
399 assert_json_eq!(
400 object,
401 json!({
402 "type": "string",
403 "deprecated": true,
404 "readOnly": true,
405 "writeOnly": false,
406 "xml": {},
407 "minLength": 1,
408 "maxLength": 10,
409 "pattern": "^[a-z]+$"
410 })
411 );
412 }
413
414 #[test]
415 fn test_build_number_object() {
416 let object = Object::new()
417 .schema_type(BasicType::Number)
418 .deprecated(Deprecated::True)
419 .write_only(false)
420 .read_only(true)
421 .xml(Xml::new())
422 .multiple_of(10.0)
423 .minimum(0.0)
424 .maximum(1000.0)
425 .exclusive_minimum(0.0)
426 .exclusive_maximum(1000.0);
427
428 assert_json_eq!(
429 object,
430 json!({
431 "type": "number",
432 "deprecated": true,
433 "readOnly": true,
434 "writeOnly": false,
435 "xml": {},
436 "multipleOf": 10.0,
437 "minimum": 0.0,
438 "maximum": 1000.0,
439 "exclusiveMinimum": 0.0,
440 "exclusiveMaximum": 1000.0
441 })
442 );
443 }
444
445 #[test]
446 fn test_build_object_object() {
447 let object = Object::new()
448 .schema_type(BasicType::Object)
449 .deprecated(Deprecated::True)
450 .write_only(false)
451 .read_only(true)
452 .xml(Xml::new())
453 .min_properties(1)
454 .max_properties(10);
455
456 assert_json_eq!(
457 object,
458 json!({
459 "type": "object",
460 "deprecated": true,
461 "readOnly": true,
462 "writeOnly": false,
463 "xml": {},
464 "minProperties": 1,
465 "maxProperties": 10
466 })
467 );
468 }
469
470 #[test]
471 fn test_object_with_extensions() {
472 let expected = json!("value");
473 let json_value = Object::new().add_extension("x-some-extension", expected.clone());
474
475 let value = serde_json::to_value(&json_value).unwrap();
476 assert_eq!(value.get("x-some-extension"), Some(&expected));
477 }
478}