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
1451 let id = credential.get("id").unwrap().as_object().unwrap();
1452 assert_eq!(
1453 id.get("default").unwrap().as_number().unwrap().as_i64().unwrap(),
1454 1,
1455 "components.schemas.Credential.properties.id.default did not match"
1456 );
1457 assert_eq!(
1458 id.get("description").unwrap().as_str().unwrap(),
1459 "Id of credential",
1460 "components.schemas.Credential.properties.id.description did not match"
1461 );
1462 assert_eq!(
1463 id.get("format").unwrap().as_str().unwrap(),
1464 "int32",
1465 "components.schemas.Credential.properties.id.format did not match"
1466 );
1467 assert_eq!(
1468 id.get("type").unwrap().as_str().unwrap(),
1469 "integer",
1470 "components.schemas.Credential.properties.id.type did not match"
1471 );
1472
1473 let name = credential.get("name").unwrap().as_object().unwrap();
1474 assert_eq!(
1475 name.get("description").unwrap().as_str().unwrap(),
1476 "Name of credential",
1477 "components.schemas.Credential.properties.name.description did not match"
1478 );
1479 assert_eq!(
1480 name.get("type").unwrap().as_str().unwrap(),
1481 "string",
1482 "components.schemas.Credential.properties.name.type did not match"
1483 );
1484
1485 let status = credential.get("status").unwrap().as_object().unwrap();
1486 assert_eq!(
1487 status.get("default").unwrap().as_str().unwrap(),
1488 "Active",
1489 "components.schemas.Credential.properties.status.default did not match"
1490 );
1491 assert_eq!(
1492 status.get("description").unwrap().as_str().unwrap(),
1493 "Credential status",
1494 "components.schemas.Credential.properties.status.description did not match"
1495 );
1496 assert_eq!(
1497 status.get("enum").unwrap().to_string(),
1498 r#"["Active","NotActive","Locked","Expired"]"#,
1499 "components.schemas.Credential.properties.status.enum did not match"
1500 );
1501 assert_eq!(
1502 status.get("type").unwrap().as_str().unwrap(),
1503 "string",
1504 "components.schemas.Credential.properties.status.type did not match"
1505 );
1506
1507 let history = credential.get("history").unwrap().as_object().unwrap();
1508 assert_eq!(
1509 history.get("items").unwrap().to_string(),
1510 r###"{"$ref":"#/components/schemas/UpdateHistory"}"###,
1511 "components.schemas.Credential.properties.history.items did not match"
1512 );
1513 assert_eq!(
1514 history.get("type").unwrap().as_str().unwrap(),
1515 "array",
1516 "components.schemas.Credential.properties.history.type did not match"
1517 );
1518
1519 assert_eq!(
1520 person.to_string(),
1521 r###"{"$ref":"#/components/PersonModel"}"###,
1522 "components.schemas.Person.ref did not match"
1523 );
1524
1525 Ok(())
1526 }
1527
1528 #[test]
1530 fn test_property_order() {
1531 let json_value = Object::builder()
1532 .property(
1533 "id",
1534 Object::builder()
1535 .schema_type(Type::Integer)
1536 .format("int32")
1537 .description("Id of credential")
1538 .default(1i32),
1539 )
1540 .property(
1541 "name",
1542 Object::builder().schema_type(Type::String).description("Name of credential"),
1543 )
1544 .property(
1545 "status",
1546 Object::builder()
1547 .schema_type(Type::String)
1548 .default("Active")
1549 .description("Credential status")
1550 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1551 )
1552 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1553 .property("tags", Object::builder().schema_type(Type::String).to_array())
1554 .build();
1555
1556 assert_eq!(
1557 json_value.properties.keys().collect::<Vec<_>>(),
1558 vec!["id", "name", "status", "history", "tags"]
1559 );
1560 }
1561
1562 #[test]
1564 fn test_additional_properties() {
1565 let json_value = Object::builder()
1566 .schema_type(Type::Object)
1567 .additional_properties(Object::builder().schema_type(Type::String))
1568 .build();
1569 assert_json_snapshot!(json_value, @r#"
1570 {
1571 "additionalProperties": {
1572 "type": "string"
1573 },
1574 "type": "object"
1575 }
1576 "#);
1577
1578 let json_value = Object::builder()
1579 .schema_type(Type::Object)
1580 .additional_properties(Object::builder().schema_type(Type::Number).to_array())
1581 .build();
1582
1583 assert_json_snapshot!(json_value, @r#"
1584 {
1585 "additionalProperties": {
1586 "items": {
1587 "type": "number"
1588 },
1589 "type": "array"
1590 },
1591 "type": "object"
1592 }
1593 "#);
1594
1595 let json_value = Object::builder()
1596 .schema_type(Type::Object)
1597 .additional_properties(Ref::from_schema_name("ComplexModel"))
1598 .build();
1599 assert_json_snapshot!(json_value, @r##"
1600 {
1601 "additionalProperties": {
1602 "$ref": "#/components/schemas/ComplexModel"
1603 },
1604 "type": "object"
1605 }
1606 "##);
1607 }
1608
1609 #[test]
1610 fn test_object_with_title() {
1611 let json_value = Object::builder().schema_type(Type::Object).title("SomeName").build();
1612 assert_json_snapshot!(json_value, @r#"
1613 {
1614 "title": "SomeName",
1615 "type": "object"
1616 }
1617 "#);
1618 }
1619
1620 #[test]
1621 fn derive_object_with_examples() {
1622 let json_value = Object::builder()
1623 .schema_type(Type::Object)
1624 .examples([json!({"age": 20, "name": "bob the cat"})])
1625 .build();
1626 assert_json_snapshot!(json_value, @r#"
1627 {
1628 "examples": [
1629 {
1630 "age": 20,
1631 "name": "bob the cat"
1632 }
1633 ],
1634 "type": "object"
1635 }
1636 "#);
1637 }
1638
1639 fn get_json_path<'a>(value: &'a Value, path: &str) -> &'a Value {
1640 path.split('.').fold(value, |acc, fragment| {
1641 acc.get(fragment).unwrap_or(&serde_json::value::Value::Null)
1642 })
1643 }
1644
1645 #[test]
1646 fn test_array_new() {
1647 let array = Object::builder()
1648 .property(
1649 "id",
1650 Object::builder()
1651 .schema_type(Type::Integer)
1652 .format("int32")
1653 .description("Id of credential")
1654 .default(json!(1i32)),
1655 )
1656 .to_array()
1657 .build();
1658
1659 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1660 }
1661
1662 #[test]
1663 fn test_array_builder() {
1664 let array = Object::builder()
1665 .schema_type(Type::Array)
1666 .items(
1667 Object::builder().property(
1668 "id",
1669 Object::builder()
1670 .schema_type(Type::Integer)
1671 .format("int32")
1672 .description("Id of credential")
1673 .default(1i32),
1674 ),
1675 )
1676 .build();
1677
1678 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1679 }
1680
1681 #[test]
1682 fn reserialize_deserialized_schema_components() {
1683 let components = Components::builder()
1684 .schemas_from_iter([(
1685 "Comp",
1686 Schema::from(
1687 Object::builder()
1688 .property("name", Object::builder().schema_type(Type::String))
1689 .required(["name"]),
1690 ),
1691 )])
1692 .responses_from_iter(vec![("200", Response::builder().description("Okay").build())])
1693 .security_scheme(
1694 "TLS",
1695 SecurityScheme::MutualTls {
1696 description: None,
1697 extensions: None,
1698 },
1699 )
1700 .build();
1701
1702 let serialized_components = serde_json::to_string(&components).unwrap();
1703
1704 let deserialized_components: Components = serde_json::from_str(serialized_components.as_str()).unwrap();
1705
1706 assert_eq!(
1707 serialized_components,
1708 serde_json::to_string(&deserialized_components).unwrap()
1709 )
1710 }
1711
1712 #[test]
1713 fn reserialize_deserialized_object_component() {
1714 let prop = Object::builder()
1715 .property("name", Object::builder().schema_type(Type::String))
1716 .required(["name"])
1717 .build();
1718
1719 let serialized_components = serde_json::to_string(&prop).unwrap();
1720 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1721
1722 assert_eq!(
1723 serialized_components,
1724 serde_json::to_string(&deserialized_components).unwrap()
1725 )
1726 }
1727
1728 #[test]
1729 fn reserialize_deserialized_property() {
1730 let prop = Object::builder().schema_type(Type::String).build();
1731
1732 let serialized_components = serde_json::to_string(&prop).unwrap();
1733 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1734
1735 assert_eq!(
1736 serialized_components,
1737 serde_json::to_string(&deserialized_components).unwrap()
1738 )
1739 }
1740
1741 #[test]
1742 fn deserialize_reserialize_one_of_default_type() {
1743 let a = Object::builder()
1744 .one_ofs([
1745 Object::builder().property("element", Ref::new("#/test")),
1746 Object::builder().property("foobar", Ref::new("#/foobar")),
1747 ])
1748 .build();
1749
1750 let serialized_json = serde_json::to_string(&a).expect("should serialize to json");
1751 let b: Object = serde_json::from_str(&serialized_json).expect("should deserialize OneOf");
1752 let reserialized_json = serde_json::to_string(&b).expect("reserialized json");
1753
1754 println!("{serialized_json}");
1755 println!("{reserialized_json}",);
1756 assert_eq!(serialized_json, reserialized_json);
1757 }
1758
1759 #[test]
1760 fn serialize_deserialize_any_of_of_within_ref_or_t_object_builder() {
1761 let ref_or_schema = Object::builder()
1762 .property(
1763 "test",
1764 Object::builder()
1765 .any_ofs([
1766 Object::builder().property("element", Ref::new("#/test")).build().to_array(),
1767 Object::builder().property("foobar", Ref::new("#/foobar")).build(),
1768 ])
1769 .build(),
1770 )
1771 .build();
1772
1773 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1774 println!("----------------------------");
1775 println!("{json_str}");
1776
1777 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1778
1779 let json_de_str = serde_json::to_string(&deserialized).expect("");
1780 println!("----------------------------");
1781 println!("{json_de_str}");
1782 assert!(json_str.contains("\"anyOf\""));
1783 assert_eq!(json_str, json_de_str);
1784 }
1785
1786 #[test]
1787 fn serialize_deserialize_schema_array_ref_or_t() {
1788 let ref_or_schema = Object::builder()
1789 .property("element", Ref::new("#/test"))
1790 .to_array()
1791 .to_array()
1792 .build();
1793
1794 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1795 println!("----------------------------");
1796 println!("{json_str}");
1797
1798 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1799
1800 let json_de_str = serde_json::to_string(&deserialized).expect("");
1801 println!("----------------------------");
1802 println!("{json_de_str}");
1803
1804 assert_eq!(json_str, json_de_str);
1805 }
1806
1807 #[test]
1808 fn serialize_deserialize_schema_array_builder() {
1809 let ref_or_schema = Object::builder().property("element", Ref::new("#/test")).build().to_array();
1810
1811 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1812 println!("----------------------------");
1813 println!("{json_str}");
1814
1815 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1816
1817 let json_de_str = serde_json::to_string(&deserialized).expect("");
1818 println!("----------------------------");
1819 println!("{json_de_str}");
1820
1821 assert_eq!(json_str, json_de_str);
1822 }
1823
1824 #[test]
1825 fn serialize_deserialize_schema_with_additional_properties() {
1826 let schema = Object::builder()
1827 .property("map", Object::builder().additional_properties(true))
1828 .build();
1829
1830 let json_str = serde_json::to_string(&schema).unwrap();
1831 println!("----------------------------");
1832 println!("{json_str}");
1833
1834 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1835
1836 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1837 println!("----------------------------");
1838 println!("{json_de_str}");
1839
1840 assert_eq!(json_str, json_de_str);
1841 }
1842
1843 #[test]
1844 fn serialize_deserialize_schema_with_additional_properties_object() {
1845 let schema = Object::builder()
1846 .property(
1847 "map",
1848 Object::builder()
1849 .additional_properties(Object::builder().property("name", Object::builder().schema_type(Type::String))),
1850 )
1851 .build();
1852
1853 let json_str = serde_json::to_string(&schema).unwrap();
1854 println!("----------------------------");
1855 println!("{json_str}");
1856
1857 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1858
1859 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1860 println!("----------------------------");
1861 println!("{json_de_str}");
1862
1863 assert_eq!(json_str, json_de_str);
1864 }
1865
1866 #[test]
1867 fn serialize_discriminator_with_mapping() {
1868 let mut discriminator = Discriminator::new("type");
1869 discriminator.mapping = [("int".to_string(), "#/components/schemas/MyInt".to_string())]
1870 .into_iter()
1871 .collect::<IndexMap<_, _>>();
1872 let one_of = Object::builder()
1873 .one_of(Ref::from_schema_name("MyInt"))
1874 .discriminator(discriminator)
1875 .build();
1876 assert_json_snapshot!(one_of, @r##"
1877 {
1878 "oneOf": [
1879 {
1880 "$ref": "#/components/schemas/MyInt"
1881 }
1882 ],
1883 "discriminator": {
1884 "propertyName": "type",
1885 "mapping": {
1886 "int": "#/components/schemas/MyInt"
1887 }
1888 }
1889 }
1890 "##);
1891 }
1892
1893 #[test]
1894 fn serialize_deserialize_object_with_multiple_schema_types() {
1895 let object = Object::builder().schema_type(vec![Type::Object, Type::Null]).build();
1896
1897 let json_str = serde_json::to_string(&object).unwrap();
1898 println!("----------------------------");
1899 println!("{json_str}");
1900
1901 let deserialized: Object = serde_json::from_str(&json_str).unwrap();
1902
1903 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1904 println!("----------------------------");
1905 println!("{json_de_str}");
1906
1907 assert_eq!(json_str, json_de_str);
1908 }
1909
1910 #[test]
1911 fn object_with_extensions() {
1912 let expected = json!("value");
1913 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1914 let json_value = Object::builder().extensions(extensions).build();
1915
1916 let value = serde_json::to_value(&json_value).unwrap();
1917 assert_eq!(value.get("x-some-extension"), Some(&expected));
1918 }
1919
1920 #[test]
1921 fn array_with_extensions() {
1922 let expected = json!("value");
1923 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1924 let json_value = Object::builder().extensions(extensions).to_array().build();
1925
1926 let value = serde_json::to_value(&json_value).unwrap();
1927 assert_eq!(value["items"].get("x-some-extension"), Some(&expected));
1928 }
1929
1930 #[test]
1931 fn oneof_with_extensions() {
1932 let expected = json!("value");
1933 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1934 let json_value = Object::builder()
1935 .one_of(Object::builder().extensions(extensions).build())
1936 .build();
1937
1938 let value = serde_json::to_value(&json_value).unwrap();
1939 assert_eq!(value["oneOf"][0].get("x-some-extension"), Some(&expected));
1940 }
1941
1942 #[test]
1943 fn allof_with_extensions() {
1944 let expected = json!("value");
1945 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1946 let json_value = Object::builder()
1947 .all_of(Object::builder().extensions(extensions).build())
1948 .build();
1949
1950 let value = serde_json::to_value(&json_value).unwrap();
1951 assert_eq!(value["allOf"][0].get("x-some-extension"), Some(&expected));
1952 }
1953
1954 #[test]
1955 fn anyof_with_extensions() {
1956 let expected = json!("value");
1957 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1958 let json_value = Object::builder()
1959 .any_of(Object::builder().extensions(extensions).build())
1960 .build();
1961
1962 let value = serde_json::to_value(&json_value).unwrap();
1963 assert_eq!(value["anyOf"][0].get("x-some-extension"), Some(&expected));
1964 }
1965
1966 #[test]
1967 fn merge_objects_with_not_enum_values() {
1968 let main_obj = Schema::object(
1969 Object::builder()
1970 .one_ofs([
1971 Schema::object(Object::builder().schema_type(Type::Number).build()),
1972 Schema::object(
1973 Object::builder()
1974 .schema_type(Type::String)
1975 .enum_values(vec![
1976 serde_json::Value::from("Infinity"),
1977 serde_json::Value::from("-Infinity"),
1978 serde_json::Value::from("NaN"),
1979 ])
1980 .build(),
1981 ),
1982 ])
1983 .build(),
1984 );
1985
1986 let not_nan = Schema::object(
1987 Object::builder()
1988 .not(Schema::object(
1989 Object::builder()
1990 .schema_type(Type::String)
1991 .enum_values(vec![serde_json::Value::from("NaN")])
1992 .build(),
1993 ))
1994 .build(),
1995 );
1996
1997 let not_infinity = Schema::object(
1998 Object::builder()
1999 .not(Schema::object(
2000 Object::builder()
2001 .schema_type(Type::String)
2002 .enum_values(vec![serde_json::Value::from("Infinity")])
2003 .build(),
2004 ))
2005 .build(),
2006 );
2007
2008 let schemas = vec![main_obj, not_nan, not_infinity];
2009 let merged = Object::all_ofs(schemas).into_optimized();
2010
2011 assert_json_snapshot!(merged, @r#"
2012 {
2013 "oneOf": [
2014 {
2015 "type": "number"
2016 },
2017 {
2018 "enum": [
2019 "Infinity",
2020 "-Infinity",
2021 "NaN"
2022 ],
2023 "type": "string"
2024 }
2025 ],
2026 "not": {
2027 "enum": [
2028 "NaN",
2029 "Infinity"
2030 ],
2031 "type": "string"
2032 }
2033 }
2034 "#);
2035 }
2036
2037 #[test]
2038 fn merge_objects_with_not_consts() {
2039 let not_a = Schema::object(
2040 Object::builder()
2041 .not(Schema::object(
2042 Object::builder()
2043 .schema_type(Type::String)
2044 .const_value(serde_json::Value::from("A"))
2045 .build(),
2046 ))
2047 .build(),
2048 );
2049
2050 let not_b = Schema::object(
2051 Object::builder()
2052 .not(Schema::object(
2053 Object::builder()
2054 .schema_type(Type::String)
2055 .const_value(serde_json::Value::from("B"))
2056 .build(),
2057 ))
2058 .build(),
2059 );
2060
2061 let schemas = vec![not_a, not_b];
2062 let merged = Object::all_ofs(schemas).into_optimized();
2063
2064 assert_json_snapshot!(merged, @r#"
2065 {
2066 "not": {
2067 "enum": [
2068 "B",
2069 "A"
2070 ],
2071 "type": "string"
2072 }
2073 }
2074 "#);
2075 }
2076
2077 #[test]
2078 fn dont_merge_objects_with_not_if_impossible() {
2079 let not_format_a = Schema::object(
2080 Object::builder()
2081 .not(Schema::object(
2082 Object::builder().schema_type(Type::String).format("email").build(),
2083 ))
2084 .build(),
2085 );
2086
2087 let not_format_b = Schema::object(
2088 Object::builder()
2089 .not(Schema::object(
2090 Object::builder().schema_type(Type::String).format("date-time").build(),
2091 ))
2092 .build(),
2093 );
2094
2095 let not_format_c = Schema::object(
2096 Object::builder()
2097 .not(Schema::object(
2098 Object::builder().schema_type(Type::String).format("ipv4").build(),
2099 ))
2100 .build(),
2101 );
2102
2103 let schemas = vec![not_format_a, not_format_b, not_format_c];
2104 let merged = Object::all_ofs(schemas).into_optimized();
2105
2106 assert_json_snapshot!(merged, @r#"
2107 {
2108 "allOf": [
2109 {
2110 "not": {
2111 "type": "string",
2112 "format": "date-time"
2113 }
2114 },
2115 {
2116 "not": {
2117 "type": "string",
2118 "format": "ipv4"
2119 }
2120 }
2121 ],
2122 "not": {
2123 "type": "string",
2124 "format": "email"
2125 }
2126 }
2127 "#);
2128 }
2129
2130 #[test]
2131 fn is_empty_works_parsed_from_json() {
2132 let schema: Schema = serde_json::from_str("{}").unwrap();
2133
2134 assert!(schema.is_empty());
2135 }
2136}