1use indexmap::IndexMap;
6use is_empty::IsEmpty;
7use ordered_float::OrderedFloat;
8use serde_derive::{Deserialize, Serialize};
9
10use super::extensions::Extensions;
11use super::security::SecurityScheme;
12use super::{RefOr, Response};
13
14pub fn empty() -> Schema {
19 Schema::object(Object::builder().default(serde_json::Value::Null).build())
20}
21
22#[non_exhaustive]
30#[derive(Serialize, Deserialize, Default, Clone, PartialEq, bon::Builder, IsEmpty)]
31#[cfg_attr(feature = "debug", derive(Debug))]
32#[serde(rename_all = "camelCase")]
33#[builder(on(_, into))]
34pub struct Components {
35 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
39 #[builder(field)]
40 #[is_empty(if = "IndexMap::is_empty")]
41 pub schemas: IndexMap<String, Schema>,
42
43 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
49 #[builder(field)]
50 #[is_empty(if = "IndexMap::is_empty")]
51 pub responses: IndexMap<String, RefOr<Response>>,
52
53 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
57 #[builder(field)]
58 #[is_empty(if = "IndexMap::is_empty")]
59 pub security_schemes: IndexMap<String, SecurityScheme>,
60
61 #[serde(skip_serializing_if = "Option::is_none", default, flatten)]
63 #[is_empty(if = "is_empty::is_option_really_empty")]
64 pub extensions: Option<Extensions>,
65}
66
67impl Components {
68 pub fn new() -> Self {
70 Self { ..Default::default() }
71 }
72
73 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(&mut self, name: N, security_scheme: S) {
80 self.security_schemes.insert(name.into(), security_scheme.into());
81 }
82
83 pub fn add_security_schemes_from_iter<N: Into<String>, S: Into<SecurityScheme>>(
88 &mut self,
89 schemas: impl IntoIterator<Item = (N, S)>,
90 ) {
91 self.security_schemes
92 .extend(schemas.into_iter().map(|(name, item)| (name.into(), item.into())));
93 }
94
95 pub fn add_schema<N: Into<String>, S: Into<Schema>>(&mut self, name: N, scheme: S) {
100 self.schemas.insert(name.into(), scheme.into());
101 }
102
103 pub fn add_schemas_from_iter<N: Into<String>, S: Into<Schema>>(&mut self, schemas: impl IntoIterator<Item = (N, S)>) {
110 self.schemas
111 .extend(schemas.into_iter().map(|(name, item)| (name.into(), item.into())));
112 }
113}
114
115impl<S: components_builder::State> ComponentsBuilder<S> {
116 pub fn schema(mut self, name: impl Into<String>, schema: impl Into<Schema>) -> Self {
120 self.schemas.insert(name.into(), schema.into());
121 self
122 }
123
124 pub fn schemas_from_iter<I: IntoIterator<Item = (S2, C)>, C: Into<Schema>, S2: Into<String>>(
142 mut self,
143 schemas: I,
144 ) -> Self {
145 self.schemas
146 .extend(schemas.into_iter().map(|(name, schema)| (name.into(), schema.into())));
147
148 self
149 }
150
151 pub fn response<S2: Into<String>, R: Into<RefOr<Response>>>(mut self, name: S2, response: R) -> Self {
156 self.responses.insert(name.into(), response.into());
157 self
158 }
159
160 pub fn responses_from_iter<I: IntoIterator<Item = (S2, R)>, S2: Into<String>, R: Into<RefOr<Response>>>(
165 mut self,
166 responses: I,
167 ) -> Self {
168 self.responses
169 .extend(responses.into_iter().map(|(name, response)| (name.into(), response.into())));
170
171 self
172 }
173
174 pub fn security_scheme<N: Into<String>, S2: Into<SecurityScheme>>(mut self, name: N, security_scheme: S2) -> Self {
181 self.security_schemes.insert(name.into(), security_scheme.into());
182
183 self
184 }
185}
186
187impl<S: components_builder::IsComplete> From<ComponentsBuilder<S>> for Components {
188 fn from(value: ComponentsBuilder<S>) -> Self {
189 value.build()
190 }
191}
192
193impl Default for Schema {
194 fn default() -> Self {
195 Schema::Bool(true)
196 }
197}
198
199#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, IsEmpty)]
204#[serde(rename_all = "camelCase")]
205#[cfg_attr(feature = "debug", derive(Debug))]
206pub struct Discriminator {
207 pub property_name: String,
210
211 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
215 #[is_empty(if = "IndexMap::is_empty")]
216 pub mapping: IndexMap<String, String>,
217
218 #[serde(skip_serializing_if = "Option::is_none", flatten)]
220 #[is_empty(if = "is_empty::is_option_really_empty")]
221 pub extensions: Option<Extensions>,
222}
223
224impl Discriminator {
225 pub fn new<I: Into<String>>(property_name: I) -> Self {
235 Self {
236 property_name: property_name.into(),
237 mapping: IndexMap::new(),
238 ..Default::default()
239 }
240 }
241
242 pub fn with_mapping<P: Into<String>, M: IntoIterator<Item = (K, V)>, K: Into<String>, V: Into<String>>(
259 property_name: P,
260 mapping: M,
261 ) -> Self {
262 Self {
263 property_name: property_name.into(),
264 mapping: IndexMap::from_iter(mapping.into_iter().map(|(key, val)| (key.into(), val.into()))),
265 ..Default::default()
266 }
267 }
268}
269
270#[non_exhaustive]
275#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, bon::Builder, IsEmpty)]
276#[cfg_attr(feature = "debug", derive(Debug))]
277#[builder(on(_, into))]
278pub struct Ref {
279 #[serde(rename = "$ref")]
281 pub ref_location: String,
282
283 #[serde(skip_serializing_if = "String::is_empty", default)]
287 #[builder(default)]
288 pub description: String,
289
290 #[serde(skip_serializing_if = "String::is_empty", default)]
293 #[builder(default)]
294 pub summary: String,
295}
296
297impl Ref {
298 pub fn new<I: Into<String>>(ref_location: I) -> Self {
301 Self {
302 ref_location: ref_location.into(),
303 ..Default::default()
304 }
305 }
306
307 pub fn from_schema_name<I: Into<String>>(schema_name: I) -> Self {
310 Self::new(format!("#/components/schemas/{}", schema_name.into()))
311 }
312
313 pub fn from_response_name<I: Into<String>>(response_name: I) -> Self {
316 Self::new(format!("#/components/responses/{}", response_name.into()))
317 }
318}
319
320impl<S: ref_builder::IsComplete> From<RefBuilder<S>> for Schema {
321 fn from(builder: RefBuilder<S>) -> Self {
322 Self::from(builder.build())
323 }
324}
325
326impl From<Ref> for Schema {
327 fn from(r: Ref) -> Self {
328 Self::object(
329 Object::builder()
330 .reference(r.ref_location)
331 .description(r.description)
332 .summary(r.summary)
333 .build(),
334 )
335 }
336}
337
338impl<T> From<T> for RefOr<T> {
339 fn from(t: T) -> Self {
340 Self::T(t)
341 }
342}
343
344#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Copy)]
347#[cfg_attr(feature = "debug", derive(Debug))]
348#[non_exhaustive]
349pub enum Type {
350 #[serde(rename = "array")]
352 Array,
353 #[serde(rename = "boolean")]
355 Boolean,
356 #[serde(rename = "integer")]
358 Integer,
359 #[serde(rename = "null")]
361 Null,
362 #[serde(rename = "number")]
364 Number,
365 #[serde(rename = "object")]
367 Object,
368 #[serde(rename = "string")]
370 String,
371}
372
373#[derive(Serialize, Deserialize, Clone, PartialEq)]
379#[cfg_attr(feature = "debug", derive(Debug))]
380#[serde(untagged)]
381pub enum Types {
382 Single(Type),
384 Multi(Vec<Type>),
386}
387
388impl From<Type> for Types {
389 fn from(value: Type) -> Self {
390 Self::Single(value)
391 }
392}
393
394impl From<Vec<Type>> for Types {
395 fn from(mut value: Vec<Type>) -> Self {
396 if value.len() == 1 {
397 Self::Single(value.remove(0))
398 } else {
399 Self::Multi(value)
400 }
401 }
402}
403
404fn is_opt_json_value_empty(t: &Option<serde_json::Value>) -> bool {
405 match t {
406 Some(j) => j.is_null(),
407 _ => true,
408 }
409}
410
411fn is_opt_bool_empty_with_default_false(t: &Option<bool>) -> bool {
412 match t {
413 None => true,
414 Some(t) => !*t,
415 }
416}
417
418#[derive(Serialize, Deserialize, Clone, PartialEq, Default, bon::Builder, IsEmpty)]
421#[serde(default, deny_unknown_fields)]
422#[builder(on(_, into))]
423#[cfg_attr(feature = "debug", derive(Debug))]
424#[non_exhaustive]
425pub struct Object {
426 #[serde(skip_serializing_if = "IndexMap::is_empty")]
430 #[builder(field)]
431 #[is_empty(if = "IndexMap::is_empty")]
432 pub properties: IndexMap<String, Schema>,
433 #[serde(skip_serializing_if = "Vec::is_empty")]
437 #[builder(field)]
438 pub examples: Vec<serde_json::Value>,
439 #[serde(rename = "prefixItems", skip_serializing_if = "Option::is_none")]
443 #[builder(field)]
444 #[is_empty(if = "is_empty::is_option_really_empty")]
445 pub prefix_items: Option<Vec<Schema>>,
446 #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
449 #[builder(field)]
450 #[is_empty(if = "is_empty::is_option_really_empty")]
451 pub enum_values: Option<Vec<serde_json::Value>>,
452 #[serde(skip_serializing_if = "Vec::is_empty")]
455 #[builder(field)]
456 pub required: Vec<String>,
457 #[serde(rename = "allOf", skip_serializing_if = "Vec::is_empty")]
460 #[builder(field)]
461 pub all_of: Vec<Schema>,
462 #[serde(rename = "anyOf", skip_serializing_if = "Option::is_none")]
465 #[builder(field)]
466 #[is_empty(if = "is_empty::is_option_really_empty")]
467 pub any_of: Option<Vec<Schema>>,
468 #[serde(rename = "oneOf", skip_serializing_if = "Option::is_none")]
471 #[builder(field)]
472 #[is_empty(if = "is_empty::is_option_really_empty")]
473 pub one_of: Option<Vec<Schema>>,
474 #[serde(rename = "$id", skip_serializing_if = "String::is_empty")]
477 #[builder(default)]
478 pub id: String,
479 #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
482 #[is_empty(if = "is_empty::is_option_really_empty")]
483 pub schema: Option<Schema>,
484 #[serde(rename = "$ref", skip_serializing_if = "String::is_empty")]
487 #[builder(default, name = "reference")]
488 pub reference: String,
489 #[serde(rename = "$comment", skip_serializing_if = "String::is_empty")]
492 #[builder(default)]
493 pub comment: String,
494 #[serde(skip_serializing_if = "String::is_empty")]
497 #[builder(default)]
498 pub title: String,
499 #[serde(skip_serializing_if = "String::is_empty")]
502 #[builder(default)]
503 pub description: String,
504 #[serde(skip_serializing_if = "String::is_empty")]
507 #[builder(default)]
508 pub summary: String,
509 #[serde(skip_serializing_if = "Option::is_none")]
512 #[is_empty(if = "is_opt_json_value_empty")]
513 pub default: Option<serde_json::Value>,
514 #[serde(rename = "readOnly", skip_serializing_if = "Option::is_none")]
517 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
518 pub read_only: Option<bool>,
519 #[serde(rename = "deprecated", skip_serializing_if = "Option::is_none")]
522 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
523 pub deprecated: Option<bool>,
524 #[serde(rename = "writeOnly", skip_serializing_if = "Option::is_none")]
527 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
528 pub write_only: Option<bool>,
529 #[serde(rename = "multipleOf", skip_serializing_if = "Option::is_none")]
532 pub multiple_of: Option<OrderedFloat<f64>>,
533 #[serde(skip_serializing_if = "Option::is_none")]
536 pub maximum: Option<OrderedFloat<f64>>,
537 #[serde(rename = "exclusiveMaximum", skip_serializing_if = "Option::is_none")]
540 pub exclusive_maximum: Option<OrderedFloat<f64>>,
541 #[serde(skip_serializing_if = "Option::is_none")]
544 pub minimum: Option<OrderedFloat<f64>>,
545 #[serde(rename = "exclusiveMinimum", skip_serializing_if = "Option::is_none")]
548 pub exclusive_minimum: Option<OrderedFloat<f64>>,
549 #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
552 pub max_length: Option<u64>,
553 #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
556 pub min_length: Option<u64>,
557 #[serde(skip_serializing_if = "Option::is_none")]
560 #[is_empty(if = "is_empty::is_option_really_empty")]
561 pub pattern: Option<String>,
562 #[serde(rename = "additionalItems", skip_serializing_if = "Option::is_none")]
565 #[is_empty(if = "is_empty::is_option_really_empty")]
566 pub additional_items: Option<Schema>,
567 #[serde(skip_serializing_if = "Option::is_none")]
570 #[is_empty(if = "is_empty::is_option_really_empty")]
571 pub items: Option<Schema>,
572 #[serde(rename = "maxItems", skip_serializing_if = "Option::is_none")]
575 pub max_items: Option<u64>,
576 #[serde(rename = "minItems", skip_serializing_if = "Option::is_none")]
579 pub min_items: Option<u64>,
580 #[serde(rename = "uniqueItems", skip_serializing_if = "Option::is_none")]
583 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
584 pub unique_items: Option<bool>,
585 #[serde(skip_serializing_if = "Option::is_none")]
588 #[is_empty(if = "is_empty::is_option_really_empty")]
589 pub contains: Option<Schema>,
590 #[serde(rename = "maxProperties", skip_serializing_if = "Option::is_none")]
593 pub max_properties: Option<u64>,
594 #[serde(rename = "minProperties", skip_serializing_if = "Option::is_none")]
597 pub min_properties: Option<u64>,
598 #[serde(rename = "maxContains", skip_serializing_if = "Option::is_none")]
601 pub max_contains: Option<u64>,
602 #[serde(rename = "minContains", skip_serializing_if = "Option::is_none")]
605 pub min_contains: Option<u64>,
606 #[serde(rename = "additionalProperties", skip_serializing_if = "Option::is_none")]
609 #[is_empty(if = "is_empty::is_option_really_empty")]
610 pub additional_properties: Option<Schema>,
611 #[serde(skip_serializing_if = "IndexMap::is_empty")]
614 #[builder(default)]
615 #[is_empty(if = "IndexMap::is_empty")]
616 pub definitions: IndexMap<String, Schema>,
617 #[serde(rename = "patternProperties", skip_serializing_if = "IndexMap::is_empty")]
620 #[builder(default)]
621 #[is_empty(if = "IndexMap::is_empty")]
622 pub pattern_properties: IndexMap<String, Schema>,
623 #[serde(skip_serializing_if = "IndexMap::is_empty")]
626 #[builder(default)]
627 #[is_empty(if = "IndexMap::is_empty")]
628 pub dependencies: IndexMap<String, Schema>,
629 #[serde(rename = "propertyNames", skip_serializing_if = "Option::is_none")]
632 #[is_empty(if = "is_empty::is_option_really_empty")]
633 pub property_names: Option<Schema>,
634 #[serde(rename = "const", skip_serializing_if = "Option::is_none")]
637 #[builder(name = "const_value")]
638 #[is_empty(if = "is_opt_json_value_empty")]
639 pub const_value: Option<serde_json::Value>,
640 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
643 #[builder(name = "schema_type")]
644 pub schema_type: Option<Types>,
645 #[serde(skip_serializing_if = "String::is_empty")]
648 #[builder(default)]
649 pub format: String,
650 #[serde(rename = "contentMediaType", skip_serializing_if = "String::is_empty")]
653 #[builder(default)]
654 pub content_media_type: String,
655 #[serde(rename = "contentEncoding", skip_serializing_if = "String::is_empty")]
658 #[builder(default)]
659 pub content_encoding: String,
660 #[serde(rename = "contentSchema", skip_serializing_if = "Option::is_none")]
663 #[is_empty(if = "is_empty::is_option_really_empty")]
664 pub content_schema: Option<Schema>,
665 #[serde(rename = "if", skip_serializing_if = "Option::is_none")]
668 #[is_empty(if = "is_empty::is_option_really_empty")]
669 pub if_cond: Option<Schema>,
670 #[serde(skip_serializing_if = "Option::is_none")]
673 #[builder(name = "then_cond")]
674 #[is_empty(if = "is_empty::is_option_really_empty")]
675 pub then: Option<Schema>,
676 #[serde(rename = "else", skip_serializing_if = "Option::is_none")]
679 #[is_empty(if = "is_empty::is_option_really_empty")]
680 pub else_cond: Option<Schema>,
681 #[serde(skip_serializing_if = "Option::is_none")]
684 #[is_empty(if = "is_empty::is_option_really_empty")]
685 pub not: Option<Schema>,
686 #[serde(rename = "unevaluatedItems", skip_serializing_if = "Option::is_none")]
689 #[is_empty(if = "is_empty::is_option_really_empty")]
690 pub unevaluated_items: Option<Schema>,
691 #[serde(rename = "unevaluatedProperties", skip_serializing_if = "Option::is_none")]
694 #[is_empty(if = "is_empty::is_option_really_empty")]
695 pub unevaluated_properties: Option<Schema>,
696 #[serde(skip_serializing_if = "Option::is_none")]
699 #[is_empty(if = "is_empty::is_option_really_empty")]
700 pub discriminator: Option<Discriminator>,
701 #[serde(flatten)]
703 #[is_empty(if = "is_empty::is_option_really_empty")]
704 pub extensions: Option<Extensions>,
705}
706
707impl From<Ref> for Object {
708 fn from(value: Ref) -> Self {
709 Self::builder()
710 .reference(value.ref_location)
711 .description(value.description)
712 .summary(value.summary)
713 .build()
714 }
715}
716
717impl<S: object_builder::State> ObjectBuilder<S> {
718 pub fn properties<P: Into<String>, C: Into<Schema>>(mut self, properties: impl IntoIterator<Item = (P, C)>) -> Self {
720 self.properties
721 .extend(properties.into_iter().map(|(p, s)| (p.into(), s.into())));
722 self
723 }
724
725 pub fn property(mut self, name: impl Into<String>, schema: impl Into<Schema>) -> Self {
727 self.properties.insert(name.into(), schema.into());
728 self
729 }
730
731 pub fn all_of(mut self, all_of: impl Into<Schema>) -> Self {
733 self.all_of.push(all_of.into());
734 self
735 }
736
737 pub fn all_ofs<C: Into<Schema>>(mut self, all_ofs: impl IntoIterator<Item = C>) -> Self {
739 self.all_of.extend(all_ofs.into_iter().map(|s| s.into()));
740 self
741 }
742
743 pub fn any_ofs<C: Into<Schema>>(self, any_ofs: impl IntoIterator<Item = C>) -> Self {
745 any_ofs.into_iter().fold(self, |this, c| this.any_of(c))
746 }
747
748 pub fn any_of(mut self, any_of: impl Into<Schema>) -> Self {
750 self.any_of.get_or_insert_default().push(any_of.into());
751 self
752 }
753
754 pub fn one_ofs<C: Into<Schema>>(self, one_ofs: impl IntoIterator<Item = C>) -> Self {
756 one_ofs.into_iter().fold(self, |this, c| this.one_of(c))
757 }
758
759 pub fn one_of(mut self, one_of: impl Into<Schema>) -> Self {
761 self.one_of.get_or_insert_default().push(one_of.into());
762 self
763 }
764
765 pub fn enum_value(mut self, enum_value: impl Into<serde_json::Value>) -> Self {
767 self.enum_values.get_or_insert_default().push(enum_value.into());
768 self
769 }
770
771 pub fn enum_values<E: Into<serde_json::Value>>(self, enum_values: impl IntoIterator<Item = E>) -> Self {
773 enum_values.into_iter().fold(self, |this, e| this.enum_value(e))
774 }
775
776 pub fn require(mut self, require: impl Into<String>) -> Self {
778 self.required.push(require.into());
779 self
780 }
781
782 pub fn required<R: Into<String>>(self, required: impl IntoIterator<Item = R>) -> Self {
784 required.into_iter().fold(self, |this, e| this.require(e))
785 }
786
787 pub fn example(mut self, example: impl Into<serde_json::Value>) -> Self {
789 self.examples.push(example.into());
790 self
791 }
792
793 pub fn examples<E: Into<serde_json::Value>>(self, examples: impl IntoIterator<Item = E>) -> Self {
795 examples.into_iter().fold(self, |this, e| this.example(e))
796 }
797}
798
799impl<S: object_builder::IsComplete> ObjectBuilder<S> {
800 pub fn to_array(self) -> ObjectBuilder<object_builder::SetItems<object_builder::SetSchemaType>> {
802 Object::builder().schema_type(Type::Array).items(self)
803 }
804}
805
806impl<S: object_builder::IsComplete> From<ObjectBuilder<S>> for Object {
807 fn from(value: ObjectBuilder<S>) -> Self {
808 value.build()
809 }
810}
811
812impl<S: object_builder::IsComplete> From<ObjectBuilder<S>> for Schema {
813 fn from(value: ObjectBuilder<S>) -> Self {
814 value.build().into()
815 }
816}
817
818impl Object {
819 pub fn with_type(ty: impl Into<Types>) -> ObjectBuilder<object_builder::SetSchemaType> {
829 Object::builder().schema_type(ty)
830 }
831
832 pub fn int32() -> Object {
834 Object::builder()
835 .schema_type(Type::Integer)
836 .maximum(i32::MAX as f64)
837 .minimum(i32::MIN as f64)
838 .build()
839 }
840
841 pub fn int64() -> Object {
843 Object::builder()
844 .schema_type(Type::Integer)
845 .maximum(i64::MAX as f64)
846 .minimum(i64::MIN as f64)
847 .build()
848 }
849
850 pub fn uint32() -> Object {
852 Object::builder()
853 .schema_type(Type::Integer)
854 .maximum(u32::MAX as f64)
855 .minimum(u32::MIN as f64)
856 .build()
857 }
858
859 pub fn uint64() -> Object {
861 Object::builder()
862 .schema_type(Type::Integer)
863 .maximum(u64::MAX as f64)
864 .minimum(u64::MIN as f64)
865 .build()
866 }
867
868 pub fn to_array(self) -> Self {
870 Self::builder().schema_type(Type::Array).items(self).build()
871 }
872
873 pub fn all_ofs<S: Into<Schema>>(all_ofs: impl IntoIterator<Item = S>) -> Object {
883 Object::builder().all_ofs(all_ofs).build()
884 }
885}
886
887macro_rules! iter_chain {
888 ($($item:expr),*$(,)?) => {
889 std::iter::empty()
890 $(.chain($item))*
891 };
892}
893
894macro_rules! merge_item {
895 ([$self:ident, $other:ident] => { $($item:ident => $merge_behaviour:expr),*$(,)? }) => {$({
896 let self_item = &mut $self.$item;
897 let other_item = &mut $other.$item;
898 if self_item.is_empty() {
899 *self_item = std::mem::take(other_item);
900 } else if self_item == other_item {
901 std::mem::take(other_item);
902 } else if !other_item.is_empty() {
903 $merge_behaviour(self_item, other_item);
904 }
905 })*};
906}
907
908fn dedupe_array<T: PartialEq>(items: &mut Vec<T>) {
909 let mut dedupe = Vec::new();
910 for item in items.drain(..) {
911 if !dedupe.contains(&item) {
912 dedupe.push(item);
913 }
914 }
915
916 *items = dedupe;
917}
918
919impl Object {
920 pub fn optimize(&mut self) {
923 let mut all_ofs = Vec::new();
925 self.take_all_ofs(&mut all_ofs);
926
927 all_ofs
928 .iter_mut()
929 .filter_map(|schema| schema.as_object_mut())
930 .for_each(|schema| self.merge(schema));
931
932 let sub_schemas = iter_chain!(
934 self.schema.iter_mut(),
935 self.additional_items.iter_mut(),
936 self.contains.iter_mut(),
937 self.additional_properties.iter_mut(),
938 self.items.iter_mut(),
939 self.prefix_items.iter_mut().flatten(),
940 self.definitions.values_mut(),
941 self.properties.values_mut(),
942 self.pattern_properties.values_mut(),
943 self.dependencies.values_mut(),
944 self.property_names.iter_mut(),
945 self.if_cond.iter_mut(),
946 self.then.iter_mut(),
947 self.else_cond.iter_mut(),
948 self.any_of.iter_mut().flatten(),
949 self.one_of.iter_mut().flatten(),
950 self.not.iter_mut(),
951 self.unevaluated_items.iter_mut(),
952 self.unevaluated_properties.iter_mut(),
953 self.content_schema.iter_mut(),
954 );
955
956 for schema in sub_schemas {
957 schema.optimize();
958 }
959
960 self.all_of = all_ofs.into_iter().filter(|schema| !schema.is_empty()).collect();
961 dedupe_array(&mut self.examples);
962 dedupe_array(&mut self.required);
963 if let Some(_enum) = &mut self.enum_values {
964 dedupe_array(_enum);
965 }
966 dedupe_array(&mut self.all_of);
967 if let Some(any_of) = &mut self.any_of {
968 dedupe_array(any_of);
969 }
970 if let Some(one_of) = &mut self.one_of {
971 dedupe_array(one_of);
972 }
973 }
974
975 pub fn into_optimized(mut self) -> Self {
977 self.optimize();
978 self
979 }
980
981 fn take_all_ofs(&mut self, collection: &mut Vec<Schema>) {
982 for mut schema in self.all_of.drain(..) {
983 schema.take_all_ofs(collection);
984 collection.push(schema);
985 }
986 }
987
988 fn merge(&mut self, other: &mut Self) {
989 merge_item!(
990 [self, other] => {
991 id => merge_skip,
992 schema => merge_sub_schema,
993 reference => merge_skip,
994 comment => merge_drop_second,
995 title => merge_drop_second,
996 description => merge_drop_second,
997 summary => merge_drop_second,
998 default => merge_drop_second,
999 read_only => merge_set_true,
1000 examples => merge_array_combine,
1001 multiple_of => merge_multiple_of,
1002 maximum => merge_min,
1003 exclusive_maximum => merge_min,
1004 minimum => merge_max,
1005 exclusive_minimum => merge_min,
1006 max_length => merge_min,
1007 min_length => merge_max,
1008 pattern => merge_skip,
1009 additional_items => merge_sub_schema,
1010 items => merge_sub_schema,
1011 prefix_items => merge_prefix_items,
1012 max_items => merge_min,
1013 min_items => merge_max,
1014 unique_items => merge_set_true,
1015 contains => merge_sub_schema,
1016 max_properties => merge_min,
1017 min_properties => merge_max,
1018 max_contains => merge_min,
1019 min_contains => merge_max,
1020 required => merge_array_combine,
1021 additional_properties => merge_sub_schema,
1022 definitions => merge_schema_map,
1023 properties => merge_schema_map,
1024 pattern_properties => merge_schema_map,
1025 dependencies => merge_schema_map,
1026 property_names => merge_sub_schema,
1027 const_value => merge_skip,
1028 enum_values => merge_array_union_optional,
1029 schema_type => merge_type,
1030 format => merge_skip,
1031 content_media_type => merge_skip,
1032 content_encoding => merge_skip,
1033 any_of => merge_array_combine_optional,
1037 one_of => merge_array_combine_optional,
1038 not => merge_inverted_if_possible,
1039 unevaluated_items => merge_sub_schema,
1040 unevaluated_properties => merge_sub_schema,
1041 deprecated => merge_set_true,
1042 write_only => merge_set_true,
1043 content_schema => merge_sub_schema,
1044 }
1045 );
1046 }
1047}
1048
1049fn merge_skip<T>(_: &mut T, _: &mut T) {}
1050
1051fn merge_drop_second<T: Default>(_: &mut T, other: &mut T) {
1052 std::mem::take(other);
1053}
1054
1055fn merge_min<T: Ord + Copy>(value: &mut Option<T>, other: &mut Option<T>) {
1056 let value = value.as_mut().unwrap();
1057 let other = other.take().unwrap();
1058 *value = (*value).min(other);
1059}
1060
1061fn merge_max<T: Ord + Copy>(value: &mut Option<T>, other: &mut Option<T>) {
1062 let value = value.as_mut().unwrap();
1063 let other = other.take().unwrap();
1064 *value = (*value).max(other);
1065}
1066
1067fn merge_set_true(value: &mut Option<bool>, other: &mut Option<bool>) {
1068 other.take();
1069 value.replace(true);
1070}
1071
1072fn merge_sub_schema(value: &mut Option<Schema>, other_opt: &mut Option<Schema>) {
1073 let value = value.as_mut().unwrap();
1074 let mut other = other_opt.take().unwrap();
1075 value.merge(&mut other);
1076 if !other.is_empty() {
1077 other_opt.replace(other);
1078 }
1079}
1080
1081fn merge_inverted_if_possible(value_opt: &mut Option<Schema>, other_opt: &mut Option<Schema>) {
1082 let value = value_opt.as_ref().unwrap();
1091 let other = other_opt.as_ref().unwrap();
1092 if let (Schema::Object(value_obj), Schema::Object(other_obj)) = (value, other) {
1093 let mut self_copy = (*value_obj).clone();
1094 let mut other_copy = (*other_obj).clone();
1095 {
1097 merge_item!(
1098 [self_copy, other_copy] => {
1099 id => merge_skip,
1100 schema => merge_skip,
1101 reference => merge_skip,
1102 comment => merge_skip,
1103 title => merge_skip,
1104 description => merge_skip,
1105 summary => merge_skip,
1106 default => merge_skip,
1107 read_only => merge_skip,
1108 examples => merge_skip,
1109 multiple_of => merge_skip,
1110 maximum => merge_max,
1111 exclusive_maximum => merge_max,
1112 minimum => merge_min,
1113 exclusive_minimum => merge_max,
1114 max_length => merge_max,
1115 min_length => merge_min,
1116 pattern => merge_skip,
1117 additional_items => merge_skip,
1118 items => merge_skip,
1119 prefix_items => merge_skip,
1120 max_items => merge_max,
1121 min_items => merge_min,
1122 unique_items => merge_skip,
1123 contains => merge_skip,
1124 max_properties => merge_max,
1125 min_properties => merge_min,
1126 max_contains => merge_max,
1127 min_contains => merge_min,
1128 required => merge_skip,
1129 additional_properties => merge_skip,
1130 definitions => merge_skip,
1131 properties => merge_skip,
1132 pattern_properties => merge_skip,
1133 dependencies => merge_skip,
1134 property_names => merge_skip,
1135 const_value => merge_skip,
1136 enum_values => merge_array_combine_optional,
1137 schema_type => merge_skip,
1138 format => merge_skip,
1139 content_media_type => merge_skip,
1140 content_encoding => merge_skip,
1141 any_of => merge_array_combine_optional,
1145 one_of => merge_array_combine_optional,
1146 not => merge_skip,
1147 unevaluated_items => merge_skip,
1148 unevaluated_properties => merge_skip,
1149 deprecated => merge_skip,
1150 write_only => merge_skip,
1151 content_schema => merge_skip,
1152 }
1153 );
1154 }
1155
1156 if other_copy.const_value.is_some() {
1158 let mut disallowed = self_copy.enum_values.unwrap_or_default();
1159 disallowed.push(other_copy.const_value.unwrap());
1160 other_copy.const_value = None;
1161 if self_copy.const_value.is_some() {
1162 disallowed.push(self_copy.const_value.unwrap());
1163 self_copy.const_value = None;
1164 }
1165 disallowed.dedup();
1166 self_copy.enum_values = Some(disallowed);
1167 }
1168
1169 if other_copy.is_empty() {
1171 value_opt.replace(Schema::Object(self_copy));
1172 *other_opt = Default::default();
1173 }
1174 }
1175}
1176
1177fn merge_array_combine<T: PartialEq>(value: &mut Vec<T>, other: &mut Vec<T>) {
1178 value.append(other);
1179}
1180
1181fn merge_array_union<T: PartialEq>(value: &mut Vec<T>, other: &mut Vec<T>) {
1182 let other = std::mem::take(other);
1183 value.retain(|v| other.contains(v));
1184}
1185
1186fn merge_array_union_optional<T: PartialEq>(value: &mut Option<Vec<T>>, other: &mut Option<Vec<T>>) {
1187 merge_array_union(value.as_mut().unwrap(), other.as_mut().unwrap());
1188 if other.as_ref().is_some_and(|o| o.is_empty()) {
1189 other.take();
1190 }
1191}
1192
1193fn merge_array_combine_optional<T: PartialEq>(value: &mut Option<Vec<T>>, other: &mut Option<Vec<T>>) {
1194 merge_array_combine(value.as_mut().unwrap(), other.as_mut().unwrap());
1195 if other.as_ref().is_some_and(|o| o.is_empty()) {
1196 other.take();
1197 }
1198}
1199
1200fn merge_schema_map(value: &mut IndexMap<String, Schema>, other: &mut IndexMap<String, Schema>) {
1201 for (key, mut other) in other.drain(..) {
1202 match value.entry(key) {
1203 indexmap::map::Entry::Occupied(mut value) => {
1204 value.get_mut().merge(&mut other);
1205 if !other.is_empty()
1206 && let Some(obj) = value.get_mut().as_object_mut()
1207 {
1208 obj.all_of.push(other);
1209 }
1210 }
1211 indexmap::map::Entry::Vacant(v) => {
1212 v.insert(other);
1213 }
1214 }
1215 }
1216}
1217
1218fn merge_type(value: &mut Option<Types>, other: &mut Option<Types>) {
1219 match (value.as_mut().unwrap(), other.take().unwrap()) {
1220 (Types::Single(s), Types::Single(ref o)) if s != o => {
1221 value.replace(Types::Multi(Vec::new()));
1222 }
1223 (Types::Single(_), Types::Single(_)) => {}
1224 (Types::Multi(s), Types::Multi(ref mut o)) => {
1225 merge_array_union(s, o);
1226 }
1227 (&mut Types::Single(s), Types::Multi(ref o)) | (&mut Types::Multi(ref o), Types::Single(s)) => {
1228 if o.contains(&s) {
1229 value.replace(Types::Single(s));
1230 } else {
1231 value.replace(Types::Multi(Vec::new()));
1232 }
1233 }
1234 }
1235}
1236
1237fn merge_prefix_items(value: &mut Option<Vec<Schema>>, other: &mut Option<Vec<Schema>>) {
1238 let mut other = other.take().unwrap_or_default();
1239 let value = value.as_mut().unwrap();
1240 value.extend(other.drain(value.len()..));
1241 for (value, mut other) in value.iter_mut().zip(other) {
1242 value.merge(&mut other);
1243 if !other.is_empty()
1244 && let Some(obj) = value.as_object_mut()
1245 {
1246 obj.all_of.push(other);
1247 }
1248 }
1249}
1250
1251fn merge_multiple_of(value: &mut Option<OrderedFloat<f64>>, other: &mut Option<OrderedFloat<f64>>) {
1252 let value = value.as_mut().unwrap().as_mut();
1253 let other = other.take().unwrap().into_inner();
1254
1255 fn gcd_f64(mut a: f64, mut b: f64) -> f64 {
1256 a = a.abs();
1257 b = b.abs();
1258 if a == 0.0 {
1260 return b;
1261 }
1262 if b == 0.0 {
1263 return a;
1264 }
1265 while b > 0.0 {
1267 let r = a % b;
1268 a = b;
1269 b = r;
1270 }
1271 a
1272 }
1273
1274 fn lcm_f64(a: f64, b: f64) -> f64 {
1276 if a == 0.0 || b == 0.0 {
1277 return 0.0;
1278 }
1279 let g = gcd_f64(a, b);
1280 (a / g * b).abs()
1282 }
1283
1284 *value = lcm_f64(*value, other);
1285}
1286
1287#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq)]
1289#[cfg_attr(feature = "debug", derive(Debug))]
1290#[serde(untagged)]
1291#[non_exhaustive]
1292pub enum Schema {
1293 Object(Box<Object>),
1295 Bool(bool),
1297}
1298
1299impl From<Object> for Schema {
1300 fn from(value: Object) -> Self {
1301 Self::object(value)
1302 }
1303}
1304
1305impl From<bool> for Schema {
1306 fn from(value: bool) -> Self {
1307 Self::Bool(value)
1308 }
1309}
1310
1311impl IsEmpty for Schema {
1312 fn is_empty(&self) -> bool {
1313 match self {
1314 Self::Bool(result) => *result,
1315 Self::Object(obj) => obj.is_empty(),
1316 }
1317 }
1318}
1319
1320impl Schema {
1321 pub fn to_array(self) -> Self {
1323 Self::object(Object::builder().schema_type(Type::Array).items(self))
1324 }
1325
1326 pub fn optimize(&mut self) {
1328 match self {
1329 Self::Bool(_) => {}
1330 Self::Object(obj) => obj.optimize(),
1331 }
1332 }
1333
1334 pub fn into_optimized(mut self) -> Self {
1336 match &mut self {
1337 Self::Bool(_) => {}
1338 Self::Object(obj) => obj.optimize(),
1339 }
1340 self
1341 }
1342
1343 pub fn object(value: impl Into<Object>) -> Self {
1345 Self::Object(value.into().into())
1346 }
1347
1348 fn take_all_ofs(&mut self, collection: &mut Vec<Schema>) {
1349 match self {
1350 Self::Bool(_) => {}
1351 Self::Object(obj) => obj.take_all_ofs(collection),
1352 }
1353 }
1354
1355 fn as_object_mut(&mut self) -> Option<&mut Object> {
1356 match self {
1357 Self::Bool(_) => None,
1358 Self::Object(obj) => Some(obj.as_mut()),
1359 }
1360 }
1361
1362 fn merge(&mut self, other: &mut Self) {
1363 match (self, other) {
1364 (this @ Schema::Bool(false), _) | (this, Schema::Bool(false)) => {
1365 *this = Schema::Bool(false);
1366 }
1367 (this @ Schema::Bool(true), other) => {
1368 std::mem::swap(this, other);
1369 }
1370 (_, Schema::Bool(true)) => {}
1371 (Schema::Object(value), Schema::Object(other)) => {
1372 value.merge(other.as_mut());
1373 }
1374 }
1375 }
1376}
1377
1378#[cfg(test)]
1379#[cfg_attr(coverage_nightly, coverage(off))]
1380mod tests {
1381 use insta::assert_json_snapshot;
1382 use serde_json::{Value, json};
1383
1384 use super::*;
1385 use crate::*;
1386
1387 #[test]
1388 fn create_schema_serializes_json() -> Result<(), serde_json::Error> {
1389 let openapi = OpenApi::builder()
1390 .info(Info::new("My api", "1.0.0"))
1391 .paths(Paths::new())
1392 .components(
1393 Components::builder()
1394 .schema("Person", Ref::new("#/components/PersonModel"))
1395 .schema(
1396 "Credential",
1397 Schema::from(
1398 Object::builder()
1399 .property(
1400 "id",
1401 Object::builder()
1402 .schema_type(Type::Integer)
1403 .format("int32")
1404 .description("Id of credential")
1405 .default(1i32),
1406 )
1407 .property(
1408 "name",
1409 Object::builder().schema_type(Type::String).description("Name of credential"),
1410 )
1411 .property(
1412 "status",
1413 Object::builder()
1414 .schema_type(Type::String)
1415 .default("Active")
1416 .description("Credential status")
1417 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1418 )
1419 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1420 .property("tags", Object::builder().schema_type(Type::String).build().to_array()),
1421 ),
1422 )
1423 .build(),
1424 )
1425 .build();
1426
1427 let serialized = serde_json::to_string_pretty(&openapi)?;
1428 println!("serialized json:\n {serialized}");
1429
1430 let value = serde_json::to_value(&openapi)?;
1431 let credential = get_json_path(&value, "components.schemas.Credential.properties");
1432 let person = get_json_path(&value, "components.schemas.Person");
1433
1434 assert!(
1435 credential.get("id").is_some(),
1436 "could not find path: components.schemas.Credential.properties.id"
1437 );
1438 assert!(
1439 credential.get("status").is_some(),
1440 "could not find path: components.schemas.Credential.properties.status"
1441 );
1442 assert!(
1443 credential.get("name").is_some(),
1444 "could not find path: components.schemas.Credential.properties.name"
1445 );
1446 assert!(
1447 credential.get("history").is_some(),
1448 "could not find path: components.schemas.Credential.properties.history"
1449 );
1450 assert_eq!(
1451 credential.get("id").unwrap_or(&serde_json::value::Value::Null).to_string(),
1452 r#"{"default":1,"description":"Id of credential","format":"int32","type":"integer"}"#,
1453 "components.schemas.Credential.properties.id did not match"
1454 );
1455 assert_eq!(
1456 credential.get("name").unwrap_or(&serde_json::value::Value::Null).to_string(),
1457 r#"{"description":"Name of credential","type":"string"}"#,
1458 "components.schemas.Credential.properties.name did not match"
1459 );
1460 assert_eq!(
1461 credential
1462 .get("status")
1463 .unwrap_or(&serde_json::value::Value::Null)
1464 .to_string(),
1465 r#"{"default":"Active","description":"Credential status","enum":["Active","NotActive","Locked","Expired"],"type":"string"}"#,
1466 "components.schemas.Credential.properties.status did not match"
1467 );
1468 assert_eq!(
1469 credential
1470 .get("history")
1471 .unwrap_or(&serde_json::value::Value::Null)
1472 .to_string(),
1473 r###"{"items":{"$ref":"#/components/schemas/UpdateHistory"},"type":"array"}"###,
1474 "components.schemas.Credential.properties.history did not match"
1475 );
1476 assert_eq!(
1477 person.to_string(),
1478 r###"{"$ref":"#/components/PersonModel"}"###,
1479 "components.schemas.Person.ref did not match"
1480 );
1481
1482 Ok(())
1483 }
1484
1485 #[test]
1487 fn test_property_order() {
1488 let json_value = Object::builder()
1489 .property(
1490 "id",
1491 Object::builder()
1492 .schema_type(Type::Integer)
1493 .format("int32")
1494 .description("Id of credential")
1495 .default(1i32),
1496 )
1497 .property(
1498 "name",
1499 Object::builder().schema_type(Type::String).description("Name of credential"),
1500 )
1501 .property(
1502 "status",
1503 Object::builder()
1504 .schema_type(Type::String)
1505 .default("Active")
1506 .description("Credential status")
1507 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1508 )
1509 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1510 .property("tags", Object::builder().schema_type(Type::String).to_array())
1511 .build();
1512
1513 assert_eq!(
1514 json_value.properties.keys().collect::<Vec<_>>(),
1515 vec!["id", "name", "status", "history", "tags"]
1516 );
1517 }
1518
1519 #[test]
1521 fn test_additional_properties() {
1522 let json_value = Object::builder()
1523 .schema_type(Type::Object)
1524 .additional_properties(Object::builder().schema_type(Type::String))
1525 .build();
1526 assert_json_snapshot!(json_value, @r#"
1527 {
1528 "additionalProperties": {
1529 "type": "string"
1530 },
1531 "type": "object"
1532 }
1533 "#);
1534
1535 let json_value = Object::builder()
1536 .schema_type(Type::Object)
1537 .additional_properties(Object::builder().schema_type(Type::Number).to_array())
1538 .build();
1539
1540 assert_json_snapshot!(json_value, @r#"
1541 {
1542 "additionalProperties": {
1543 "items": {
1544 "type": "number"
1545 },
1546 "type": "array"
1547 },
1548 "type": "object"
1549 }
1550 "#);
1551
1552 let json_value = Object::builder()
1553 .schema_type(Type::Object)
1554 .additional_properties(Ref::from_schema_name("ComplexModel"))
1555 .build();
1556 assert_json_snapshot!(json_value, @r##"
1557 {
1558 "additionalProperties": {
1559 "$ref": "#/components/schemas/ComplexModel"
1560 },
1561 "type": "object"
1562 }
1563 "##);
1564 }
1565
1566 #[test]
1567 fn test_object_with_title() {
1568 let json_value = Object::builder().schema_type(Type::Object).title("SomeName").build();
1569 assert_json_snapshot!(json_value, @r#"
1570 {
1571 "title": "SomeName",
1572 "type": "object"
1573 }
1574 "#);
1575 }
1576
1577 #[test]
1578 fn derive_object_with_examples() {
1579 let json_value = Object::builder()
1580 .schema_type(Type::Object)
1581 .examples([json!({"age": 20, "name": "bob the cat"})])
1582 .build();
1583 assert_json_snapshot!(json_value, @r#"
1584 {
1585 "examples": [
1586 {
1587 "age": 20,
1588 "name": "bob the cat"
1589 }
1590 ],
1591 "type": "object"
1592 }
1593 "#);
1594 }
1595
1596 fn get_json_path<'a>(value: &'a Value, path: &str) -> &'a Value {
1597 path.split('.').fold(value, |acc, fragment| {
1598 acc.get(fragment).unwrap_or(&serde_json::value::Value::Null)
1599 })
1600 }
1601
1602 #[test]
1603 fn test_array_new() {
1604 let array = Object::builder()
1605 .property(
1606 "id",
1607 Object::builder()
1608 .schema_type(Type::Integer)
1609 .format("int32")
1610 .description("Id of credential")
1611 .default(json!(1i32)),
1612 )
1613 .to_array()
1614 .build();
1615
1616 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1617 }
1618
1619 #[test]
1620 fn test_array_builder() {
1621 let array = Object::builder()
1622 .schema_type(Type::Array)
1623 .items(
1624 Object::builder().property(
1625 "id",
1626 Object::builder()
1627 .schema_type(Type::Integer)
1628 .format("int32")
1629 .description("Id of credential")
1630 .default(1i32),
1631 ),
1632 )
1633 .build();
1634
1635 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1636 }
1637
1638 #[test]
1639 fn reserialize_deserialized_schema_components() {
1640 let components = Components::builder()
1641 .schemas_from_iter([(
1642 "Comp",
1643 Schema::from(
1644 Object::builder()
1645 .property("name", Object::builder().schema_type(Type::String))
1646 .required(["name"]),
1647 ),
1648 )])
1649 .responses_from_iter(vec![("200", Response::builder().description("Okay").build())])
1650 .security_scheme(
1651 "TLS",
1652 SecurityScheme::MutualTls {
1653 description: None,
1654 extensions: None,
1655 },
1656 )
1657 .build();
1658
1659 let serialized_components = serde_json::to_string(&components).unwrap();
1660
1661 let deserialized_components: Components = serde_json::from_str(serialized_components.as_str()).unwrap();
1662
1663 assert_eq!(
1664 serialized_components,
1665 serde_json::to_string(&deserialized_components).unwrap()
1666 )
1667 }
1668
1669 #[test]
1670 fn reserialize_deserialized_object_component() {
1671 let prop = Object::builder()
1672 .property("name", Object::builder().schema_type(Type::String))
1673 .required(["name"])
1674 .build();
1675
1676 let serialized_components = serde_json::to_string(&prop).unwrap();
1677 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1678
1679 assert_eq!(
1680 serialized_components,
1681 serde_json::to_string(&deserialized_components).unwrap()
1682 )
1683 }
1684
1685 #[test]
1686 fn reserialize_deserialized_property() {
1687 let prop = Object::builder().schema_type(Type::String).build();
1688
1689 let serialized_components = serde_json::to_string(&prop).unwrap();
1690 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1691
1692 assert_eq!(
1693 serialized_components,
1694 serde_json::to_string(&deserialized_components).unwrap()
1695 )
1696 }
1697
1698 #[test]
1699 fn deserialize_reserialize_one_of_default_type() {
1700 let a = Object::builder()
1701 .one_ofs([
1702 Object::builder().property("element", Ref::new("#/test")),
1703 Object::builder().property("foobar", Ref::new("#/foobar")),
1704 ])
1705 .build();
1706
1707 let serialized_json = serde_json::to_string(&a).expect("should serialize to json");
1708 let b: Object = serde_json::from_str(&serialized_json).expect("should deserialize OneOf");
1709 let reserialized_json = serde_json::to_string(&b).expect("reserialized json");
1710
1711 println!("{serialized_json}");
1712 println!("{reserialized_json}",);
1713 assert_eq!(serialized_json, reserialized_json);
1714 }
1715
1716 #[test]
1717 fn serialize_deserialize_any_of_of_within_ref_or_t_object_builder() {
1718 let ref_or_schema = Object::builder()
1719 .property(
1720 "test",
1721 Object::builder()
1722 .any_ofs([
1723 Object::builder().property("element", Ref::new("#/test")).build().to_array(),
1724 Object::builder().property("foobar", Ref::new("#/foobar")).build(),
1725 ])
1726 .build(),
1727 )
1728 .build();
1729
1730 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1731 println!("----------------------------");
1732 println!("{json_str}");
1733
1734 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1735
1736 let json_de_str = serde_json::to_string(&deserialized).expect("");
1737 println!("----------------------------");
1738 println!("{json_de_str}");
1739 assert!(json_str.contains("\"anyOf\""));
1740 assert_eq!(json_str, json_de_str);
1741 }
1742
1743 #[test]
1744 fn serialize_deserialize_schema_array_ref_or_t() {
1745 let ref_or_schema = Object::builder()
1746 .property("element", Ref::new("#/test"))
1747 .to_array()
1748 .to_array()
1749 .build();
1750
1751 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1752 println!("----------------------------");
1753 println!("{json_str}");
1754
1755 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1756
1757 let json_de_str = serde_json::to_string(&deserialized).expect("");
1758 println!("----------------------------");
1759 println!("{json_de_str}");
1760
1761 assert_eq!(json_str, json_de_str);
1762 }
1763
1764 #[test]
1765 fn serialize_deserialize_schema_array_builder() {
1766 let ref_or_schema = Object::builder().property("element", Ref::new("#/test")).build().to_array();
1767
1768 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1769 println!("----------------------------");
1770 println!("{json_str}");
1771
1772 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1773
1774 let json_de_str = serde_json::to_string(&deserialized).expect("");
1775 println!("----------------------------");
1776 println!("{json_de_str}");
1777
1778 assert_eq!(json_str, json_de_str);
1779 }
1780
1781 #[test]
1782 fn serialize_deserialize_schema_with_additional_properties() {
1783 let schema = Object::builder()
1784 .property("map", Object::builder().additional_properties(true))
1785 .build();
1786
1787 let json_str = serde_json::to_string(&schema).unwrap();
1788 println!("----------------------------");
1789 println!("{json_str}");
1790
1791 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1792
1793 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1794 println!("----------------------------");
1795 println!("{json_de_str}");
1796
1797 assert_eq!(json_str, json_de_str);
1798 }
1799
1800 #[test]
1801 fn serialize_deserialize_schema_with_additional_properties_object() {
1802 let schema = Object::builder()
1803 .property(
1804 "map",
1805 Object::builder()
1806 .additional_properties(Object::builder().property("name", Object::builder().schema_type(Type::String))),
1807 )
1808 .build();
1809
1810 let json_str = serde_json::to_string(&schema).unwrap();
1811 println!("----------------------------");
1812 println!("{json_str}");
1813
1814 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1815
1816 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1817 println!("----------------------------");
1818 println!("{json_de_str}");
1819
1820 assert_eq!(json_str, json_de_str);
1821 }
1822
1823 #[test]
1824 fn serialize_discriminator_with_mapping() {
1825 let mut discriminator = Discriminator::new("type");
1826 discriminator.mapping = [("int".to_string(), "#/components/schemas/MyInt".to_string())]
1827 .into_iter()
1828 .collect::<IndexMap<_, _>>();
1829 let one_of = Object::builder()
1830 .one_of(Ref::from_schema_name("MyInt"))
1831 .discriminator(discriminator)
1832 .build();
1833 assert_json_snapshot!(one_of, @r##"
1834 {
1835 "oneOf": [
1836 {
1837 "$ref": "#/components/schemas/MyInt"
1838 }
1839 ],
1840 "discriminator": {
1841 "propertyName": "type",
1842 "mapping": {
1843 "int": "#/components/schemas/MyInt"
1844 }
1845 }
1846 }
1847 "##);
1848 }
1849
1850 #[test]
1851 fn serialize_deserialize_object_with_multiple_schema_types() {
1852 let object = Object::builder().schema_type(vec![Type::Object, Type::Null]).build();
1853
1854 let json_str = serde_json::to_string(&object).unwrap();
1855 println!("----------------------------");
1856 println!("{json_str}");
1857
1858 let deserialized: Object = serde_json::from_str(&json_str).unwrap();
1859
1860 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1861 println!("----------------------------");
1862 println!("{json_de_str}");
1863
1864 assert_eq!(json_str, json_de_str);
1865 }
1866
1867 #[test]
1868 fn object_with_extensions() {
1869 let expected = json!("value");
1870 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1871 let json_value = Object::builder().extensions(extensions).build();
1872
1873 let value = serde_json::to_value(&json_value).unwrap();
1874 assert_eq!(value.get("x-some-extension"), Some(&expected));
1875 }
1876
1877 #[test]
1878 fn array_with_extensions() {
1879 let expected = json!("value");
1880 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1881 let json_value = Object::builder().extensions(extensions).to_array().build();
1882
1883 let value = serde_json::to_value(&json_value).unwrap();
1884 assert_eq!(value["items"].get("x-some-extension"), Some(&expected));
1885 }
1886
1887 #[test]
1888 fn oneof_with_extensions() {
1889 let expected = json!("value");
1890 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1891 let json_value = Object::builder()
1892 .one_of(Object::builder().extensions(extensions).build())
1893 .build();
1894
1895 let value = serde_json::to_value(&json_value).unwrap();
1896 assert_eq!(value["oneOf"][0].get("x-some-extension"), Some(&expected));
1897 }
1898
1899 #[test]
1900 fn allof_with_extensions() {
1901 let expected = json!("value");
1902 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1903 let json_value = Object::builder()
1904 .all_of(Object::builder().extensions(extensions).build())
1905 .build();
1906
1907 let value = serde_json::to_value(&json_value).unwrap();
1908 assert_eq!(value["allOf"][0].get("x-some-extension"), Some(&expected));
1909 }
1910
1911 #[test]
1912 fn anyof_with_extensions() {
1913 let expected = json!("value");
1914 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1915 let json_value = Object::builder()
1916 .any_of(Object::builder().extensions(extensions).build())
1917 .build();
1918
1919 let value = serde_json::to_value(&json_value).unwrap();
1920 assert_eq!(value["anyOf"][0].get("x-some-extension"), Some(&expected));
1921 }
1922
1923 #[test]
1924 fn merge_objects_with_not_enum_values() {
1925 let main_obj = Schema::object(
1926 Object::builder()
1927 .one_ofs([
1928 Schema::object(Object::builder().schema_type(Type::Number).build()),
1929 Schema::object(
1930 Object::builder()
1931 .schema_type(Type::String)
1932 .enum_values(vec![
1933 serde_json::Value::from("Infinity"),
1934 serde_json::Value::from("-Infinity"),
1935 serde_json::Value::from("NaN"),
1936 ])
1937 .build(),
1938 ),
1939 ])
1940 .build(),
1941 );
1942
1943 let not_nan = Schema::object(
1944 Object::builder()
1945 .not(Schema::object(
1946 Object::builder()
1947 .schema_type(Type::String)
1948 .enum_values(vec![serde_json::Value::from("NaN")])
1949 .build(),
1950 ))
1951 .build(),
1952 );
1953
1954 let not_infinity = Schema::object(
1955 Object::builder()
1956 .not(Schema::object(
1957 Object::builder()
1958 .schema_type(Type::String)
1959 .enum_values(vec![serde_json::Value::from("Infinity")])
1960 .build(),
1961 ))
1962 .build(),
1963 );
1964
1965 let schemas = vec![main_obj, not_nan, not_infinity];
1966 let merged = Object::all_ofs(schemas).into_optimized();
1967
1968 assert_json_snapshot!(merged, @r#"
1969 {
1970 "oneOf": [
1971 {
1972 "type": "number"
1973 },
1974 {
1975 "enum": [
1976 "Infinity",
1977 "-Infinity",
1978 "NaN"
1979 ],
1980 "type": "string"
1981 }
1982 ],
1983 "not": {
1984 "enum": [
1985 "NaN",
1986 "Infinity"
1987 ],
1988 "type": "string"
1989 }
1990 }
1991 "#);
1992 }
1993
1994 #[test]
1995 fn merge_objects_with_not_consts() {
1996 let not_a = Schema::object(
1997 Object::builder()
1998 .not(Schema::object(
1999 Object::builder()
2000 .schema_type(Type::String)
2001 .const_value(serde_json::Value::from("A"))
2002 .build(),
2003 ))
2004 .build(),
2005 );
2006
2007 let not_b = Schema::object(
2008 Object::builder()
2009 .not(Schema::object(
2010 Object::builder()
2011 .schema_type(Type::String)
2012 .const_value(serde_json::Value::from("B"))
2013 .build(),
2014 ))
2015 .build(),
2016 );
2017
2018 let schemas = vec![not_a, not_b];
2019 let merged = Object::all_ofs(schemas).into_optimized();
2020
2021 assert_json_snapshot!(merged, @r#"
2022 {
2023 "not": {
2024 "enum": [
2025 "B",
2026 "A"
2027 ],
2028 "type": "string"
2029 }
2030 }
2031 "#);
2032 }
2033
2034 #[test]
2035 fn dont_merge_objects_with_not_if_impossible() {
2036 let not_format_a = Schema::object(
2037 Object::builder()
2038 .not(Schema::object(
2039 Object::builder().schema_type(Type::String).format("email").build(),
2040 ))
2041 .build(),
2042 );
2043
2044 let not_format_b = Schema::object(
2045 Object::builder()
2046 .not(Schema::object(
2047 Object::builder().schema_type(Type::String).format("date-time").build(),
2048 ))
2049 .build(),
2050 );
2051
2052 let not_format_c = Schema::object(
2053 Object::builder()
2054 .not(Schema::object(
2055 Object::builder().schema_type(Type::String).format("ipv4").build(),
2056 ))
2057 .build(),
2058 );
2059
2060 let schemas = vec![not_format_a, not_format_b, not_format_c];
2061 let merged = Object::all_ofs(schemas).into_optimized();
2062
2063 assert_json_snapshot!(merged, @r#"
2064 {
2065 "allOf": [
2066 {
2067 "not": {
2068 "type": "string",
2069 "format": "date-time"
2070 }
2071 },
2072 {
2073 "not": {
2074 "type": "string",
2075 "format": "ipv4"
2076 }
2077 }
2078 ],
2079 "not": {
2080 "type": "string",
2081 "format": "email"
2082 }
2083 }
2084 "#);
2085 }
2086
2087 #[test]
2088 fn is_empty_works_parsed_from_json() {
2089 let schema: Schema = serde_json::from_str("{}").unwrap();
2090
2091 assert!(schema.is_empty());
2092 }
2093}