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
14#[allow(unused_imports)]
15use super::security::SecurityRequirement;
16
17pub fn empty() -> Schema {
22 Schema::object(Object::builder().default(serde_json::Value::Null).build())
23}
24
25#[non_exhaustive]
33#[derive(Serialize, Deserialize, Default, Clone, PartialEq, bon::Builder, IsEmpty)]
34#[cfg_attr(feature = "debug", derive(Debug))]
35#[serde(rename_all = "camelCase")]
36#[builder(on(_, into))]
37pub struct Components {
38 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
42 #[builder(field)]
43 #[is_empty(if = "IndexMap::is_empty")]
44 pub schemas: IndexMap<String, Schema>,
45
46 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
52 #[builder(field)]
53 #[is_empty(if = "IndexMap::is_empty")]
54 pub responses: IndexMap<String, RefOr<Response>>,
55
56 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
60 #[builder(field)]
61 #[is_empty(if = "IndexMap::is_empty")]
62 pub security_schemes: IndexMap<String, SecurityScheme>,
63
64 #[serde(skip_serializing_if = "Option::is_none", default, flatten)]
66 #[is_empty(if = "is_empty::is_option_really_empty")]
67 pub extensions: Option<Extensions>,
68}
69
70impl Components {
71 pub fn new() -> Self {
73 Self { ..Default::default() }
74 }
75
76 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(&mut self, name: N, security_scheme: S) {
82 self.security_schemes.insert(name.into(), security_scheme.into());
83 }
84
85 pub fn add_security_schemes_from_iter<N: Into<String>, S: Into<SecurityScheme>>(
90 &mut self,
91 schemas: impl IntoIterator<Item = (N, S)>,
92 ) {
93 self.security_schemes
94 .extend(schemas.into_iter().map(|(name, item)| (name.into(), item.into())));
95 }
96
97 pub fn add_schema<N: Into<String>, S: Into<Schema>>(&mut self, name: N, scheme: S) {
102 self.schemas.insert(name.into(), scheme.into());
103 }
104
105 pub fn add_schemas_from_iter<N: Into<String>, S: Into<Schema>>(&mut self, schemas: impl IntoIterator<Item = (N, S)>) {
112 self.schemas
113 .extend(schemas.into_iter().map(|(name, item)| (name.into(), item.into())));
114 }
115}
116
117impl<S: components_builder::State> ComponentsBuilder<S> {
118 pub fn schema(mut self, name: impl Into<String>, schema: impl Into<Schema>) -> Self {
122 self.schemas.insert(name.into(), schema.into());
123 self
124 }
125
126 pub fn schemas_from_iter<I: IntoIterator<Item = (S2, C)>, C: Into<Schema>, S2: Into<String>>(
144 mut self,
145 schemas: I,
146 ) -> Self {
147 self.schemas
148 .extend(schemas.into_iter().map(|(name, schema)| (name.into(), schema.into())));
149
150 self
151 }
152
153 pub fn response<S2: Into<String>, R: Into<RefOr<Response>>>(mut self, name: S2, response: R) -> Self {
158 self.responses.insert(name.into(), response.into());
159 self
160 }
161
162 pub fn responses_from_iter<I: IntoIterator<Item = (S2, R)>, S2: Into<String>, R: Into<RefOr<Response>>>(
167 mut self,
168 responses: I,
169 ) -> Self {
170 self.responses
171 .extend(responses.into_iter().map(|(name, response)| (name.into(), response.into())));
172
173 self
174 }
175
176 pub fn security_scheme<N: Into<String>, S2: Into<SecurityScheme>>(mut self, name: N, security_scheme: S2) -> Self {
183 self.security_schemes.insert(name.into(), security_scheme.into());
184
185 self
186 }
187}
188
189impl<S: components_builder::IsComplete> From<ComponentsBuilder<S>> for Components {
190 fn from(value: ComponentsBuilder<S>) -> Self {
191 value.build()
192 }
193}
194
195impl Default for Schema {
196 fn default() -> Self {
197 Schema::Bool(true)
198 }
199}
200
201#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, IsEmpty)]
206#[serde(rename_all = "camelCase")]
207#[cfg_attr(feature = "debug", derive(Debug))]
208pub struct Discriminator {
209 pub property_name: String,
212
213 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
217 #[is_empty(if = "IndexMap::is_empty")]
218 pub mapping: IndexMap<String, String>,
219
220 #[serde(skip_serializing_if = "Option::is_none", flatten)]
222 #[is_empty(if = "is_empty::is_option_really_empty")]
223 pub extensions: Option<Extensions>,
224}
225
226impl Discriminator {
227 pub fn new<I: Into<String>>(property_name: I) -> Self {
237 Self {
238 property_name: property_name.into(),
239 mapping: IndexMap::new(),
240 ..Default::default()
241 }
242 }
243
244 pub fn with_mapping<P: Into<String>, M: IntoIterator<Item = (K, V)>, K: Into<String>, V: Into<String>>(
261 property_name: P,
262 mapping: M,
263 ) -> Self {
264 Self {
265 property_name: property_name.into(),
266 mapping: IndexMap::from_iter(mapping.into_iter().map(|(key, val)| (key.into(), val.into()))),
267 ..Default::default()
268 }
269 }
270}
271
272#[non_exhaustive]
277#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, bon::Builder, IsEmpty)]
278#[cfg_attr(feature = "debug", derive(Debug))]
279#[builder(on(_, into))]
280pub struct Ref {
281 #[serde(rename = "$ref")]
283 pub ref_location: String,
284
285 #[serde(skip_serializing_if = "String::is_empty", default)]
289 #[builder(default)]
290 pub description: String,
291
292 #[serde(skip_serializing_if = "String::is_empty", default)]
295 #[builder(default)]
296 pub summary: String,
297}
298
299impl Ref {
300 pub fn new<I: Into<String>>(ref_location: I) -> Self {
303 Self {
304 ref_location: ref_location.into(),
305 ..Default::default()
306 }
307 }
308
309 pub fn from_schema_name<I: Into<String>>(schema_name: I) -> Self {
312 Self::new(format!("#/components/schemas/{}", schema_name.into()))
313 }
314
315 pub fn from_response_name<I: Into<String>>(response_name: I) -> Self {
318 Self::new(format!("#/components/responses/{}", response_name.into()))
319 }
320}
321
322impl<S: ref_builder::IsComplete> From<RefBuilder<S>> for Schema {
323 fn from(builder: RefBuilder<S>) -> Self {
324 Self::from(builder.build())
325 }
326}
327
328impl From<Ref> for Schema {
329 fn from(r: Ref) -> Self {
330 Self::object(
331 Object::builder()
332 .reference(r.ref_location)
333 .description(r.description)
334 .summary(r.summary)
335 .build(),
336 )
337 }
338}
339
340impl<T> From<T> for RefOr<T> {
341 fn from(t: T) -> Self {
342 Self::T(t)
343 }
344}
345
346#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Copy)]
349#[cfg_attr(feature = "debug", derive(Debug))]
350#[non_exhaustive]
351pub enum Type {
352 #[serde(rename = "array")]
354 Array,
355 #[serde(rename = "boolean")]
357 Boolean,
358 #[serde(rename = "integer")]
360 Integer,
361 #[serde(rename = "null")]
363 Null,
364 #[serde(rename = "number")]
366 Number,
367 #[serde(rename = "object")]
369 Object,
370 #[serde(rename = "string")]
372 String,
373}
374
375#[derive(Serialize, Deserialize, Clone, PartialEq)]
381#[cfg_attr(feature = "debug", derive(Debug))]
382#[serde(untagged)]
383pub enum Types {
384 Single(Type),
386 Multi(Vec<Type>),
388}
389
390impl From<Type> for Types {
391 fn from(value: Type) -> Self {
392 Self::Single(value)
393 }
394}
395
396impl From<Vec<Type>> for Types {
397 fn from(mut value: Vec<Type>) -> Self {
398 if value.len() == 1 {
399 Self::Single(value.remove(0))
400 } else {
401 Self::Multi(value)
402 }
403 }
404}
405
406fn is_opt_json_value_empty(t: &Option<serde_json::Value>) -> bool {
407 match t {
408 Some(j) => j.is_null(),
409 _ => true,
410 }
411}
412
413fn is_opt_bool_empty_with_default_false(t: &Option<bool>) -> bool {
414 match t {
415 None => true,
416 Some(t) => !*t,
417 }
418}
419
420#[derive(Serialize, Deserialize, Clone, PartialEq, Default, bon::Builder, IsEmpty)]
423#[serde(default, deny_unknown_fields)]
424#[builder(on(_, into))]
425#[cfg_attr(feature = "debug", derive(Debug))]
426#[non_exhaustive]
427pub struct Object {
428 #[serde(skip_serializing_if = "IndexMap::is_empty")]
432 #[builder(field)]
433 #[is_empty(if = "IndexMap::is_empty")]
434 pub properties: IndexMap<String, Schema>,
435 #[serde(skip_serializing_if = "Vec::is_empty")]
439 #[builder(field)]
440 pub examples: Vec<serde_json::Value>,
441 #[serde(rename = "prefixItems", skip_serializing_if = "Option::is_none")]
445 #[builder(field)]
446 #[is_empty(if = "is_empty::is_option_really_empty")]
447 pub prefix_items: Option<Vec<Schema>>,
448 #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
451 #[builder(field)]
452 #[is_empty(if = "is_empty::is_option_really_empty")]
453 pub enum_values: Option<Vec<serde_json::Value>>,
454 #[serde(skip_serializing_if = "Vec::is_empty")]
457 #[builder(field)]
458 pub required: Vec<String>,
459 #[serde(rename = "allOf", skip_serializing_if = "Vec::is_empty")]
462 #[builder(field)]
463 pub all_of: Vec<Schema>,
464 #[serde(rename = "anyOf", skip_serializing_if = "Option::is_none")]
467 #[builder(field)]
468 #[is_empty(if = "is_empty::is_option_really_empty")]
469 pub any_of: Option<Vec<Schema>>,
470 #[serde(rename = "oneOf", skip_serializing_if = "Option::is_none")]
473 #[builder(field)]
474 #[is_empty(if = "is_empty::is_option_really_empty")]
475 pub one_of: Option<Vec<Schema>>,
476 #[serde(rename = "$id", skip_serializing_if = "String::is_empty")]
479 #[builder(default)]
480 pub id: String,
481 #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
484 #[is_empty(if = "is_empty::is_option_really_empty")]
485 pub schema: Option<Schema>,
486 #[serde(rename = "$ref", skip_serializing_if = "String::is_empty")]
489 #[builder(default, name = "reference")]
490 pub reference: String,
491 #[serde(rename = "$comment", skip_serializing_if = "String::is_empty")]
494 #[builder(default)]
495 pub comment: String,
496 #[serde(skip_serializing_if = "String::is_empty")]
499 #[builder(default)]
500 pub title: String,
501 #[serde(skip_serializing_if = "String::is_empty")]
504 #[builder(default)]
505 pub description: String,
506 #[serde(skip_serializing_if = "String::is_empty")]
509 #[builder(default)]
510 pub summary: String,
511 #[serde(skip_serializing_if = "Option::is_none")]
514 #[is_empty(if = "is_opt_json_value_empty")]
515 pub default: Option<serde_json::Value>,
516 #[serde(rename = "readOnly", skip_serializing_if = "Option::is_none")]
519 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
520 pub read_only: Option<bool>,
521 #[serde(rename = "deprecated", skip_serializing_if = "Option::is_none")]
524 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
525 pub deprecated: Option<bool>,
526 #[serde(rename = "writeOnly", skip_serializing_if = "Option::is_none")]
529 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
530 pub write_only: Option<bool>,
531 #[serde(rename = "multipleOf", skip_serializing_if = "Option::is_none")]
534 pub multiple_of: Option<OrderedFloat<f64>>,
535 #[serde(skip_serializing_if = "Option::is_none")]
538 pub maximum: Option<OrderedFloat<f64>>,
539 #[serde(rename = "exclusiveMaximum", skip_serializing_if = "Option::is_none")]
542 pub exclusive_maximum: Option<OrderedFloat<f64>>,
543 #[serde(skip_serializing_if = "Option::is_none")]
546 pub minimum: Option<OrderedFloat<f64>>,
547 #[serde(rename = "exclusiveMinimum", skip_serializing_if = "Option::is_none")]
550 pub exclusive_minimum: Option<OrderedFloat<f64>>,
551 #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
554 pub max_length: Option<u64>,
555 #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
558 pub min_length: Option<u64>,
559 #[serde(skip_serializing_if = "Option::is_none")]
562 #[is_empty(if = "is_empty::is_option_really_empty")]
563 pub pattern: Option<String>,
564 #[serde(rename = "additionalItems", skip_serializing_if = "Option::is_none")]
567 #[is_empty(if = "is_empty::is_option_really_empty")]
568 pub additional_items: Option<Schema>,
569 #[serde(skip_serializing_if = "Option::is_none")]
572 #[is_empty(if = "is_empty::is_option_really_empty")]
573 pub items: Option<Schema>,
574 #[serde(rename = "maxItems", skip_serializing_if = "Option::is_none")]
577 pub max_items: Option<u64>,
578 #[serde(rename = "minItems", skip_serializing_if = "Option::is_none")]
581 pub min_items: Option<u64>,
582 #[serde(rename = "uniqueItems", skip_serializing_if = "Option::is_none")]
585 #[is_empty(if = "is_opt_bool_empty_with_default_false")]
586 pub unique_items: Option<bool>,
587 #[serde(skip_serializing_if = "Option::is_none")]
590 #[is_empty(if = "is_empty::is_option_really_empty")]
591 pub contains: Option<Schema>,
592 #[serde(rename = "maxProperties", skip_serializing_if = "Option::is_none")]
595 pub max_properties: Option<u64>,
596 #[serde(rename = "minProperties", skip_serializing_if = "Option::is_none")]
599 pub min_properties: Option<u64>,
600 #[serde(rename = "maxContains", skip_serializing_if = "Option::is_none")]
603 pub max_contains: Option<u64>,
604 #[serde(rename = "minContains", skip_serializing_if = "Option::is_none")]
607 pub min_contains: Option<u64>,
608 #[serde(rename = "additionalProperties", skip_serializing_if = "Option::is_none")]
611 #[is_empty(if = "is_empty::is_option_really_empty")]
612 pub additional_properties: Option<Schema>,
613 #[serde(skip_serializing_if = "IndexMap::is_empty")]
616 #[builder(default)]
617 #[is_empty(if = "IndexMap::is_empty")]
618 pub definitions: IndexMap<String, Schema>,
619 #[serde(rename = "patternProperties", skip_serializing_if = "IndexMap::is_empty")]
622 #[builder(default)]
623 #[is_empty(if = "IndexMap::is_empty")]
624 pub pattern_properties: IndexMap<String, Schema>,
625 #[serde(skip_serializing_if = "IndexMap::is_empty")]
628 #[builder(default)]
629 #[is_empty(if = "IndexMap::is_empty")]
630 pub dependencies: IndexMap<String, Schema>,
631 #[serde(rename = "propertyNames", skip_serializing_if = "Option::is_none")]
634 #[is_empty(if = "is_empty::is_option_really_empty")]
635 pub property_names: Option<Schema>,
636 #[serde(rename = "const", skip_serializing_if = "Option::is_none")]
639 #[builder(name = "const_value")]
640 #[is_empty(if = "is_opt_json_value_empty")]
641 pub const_value: Option<serde_json::Value>,
642 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
645 #[builder(name = "schema_type")]
646 pub schema_type: Option<Types>,
647 #[serde(skip_serializing_if = "String::is_empty")]
650 #[builder(default)]
651 pub format: String,
652 #[serde(rename = "contentMediaType", skip_serializing_if = "String::is_empty")]
655 #[builder(default)]
656 pub content_media_type: String,
657 #[serde(rename = "contentEncoding", skip_serializing_if = "String::is_empty")]
660 #[builder(default)]
661 pub content_encoding: String,
662 #[serde(rename = "contentSchema", skip_serializing_if = "Option::is_none")]
665 #[is_empty(if = "is_empty::is_option_really_empty")]
666 pub content_schema: Option<Schema>,
667 #[serde(rename = "if", skip_serializing_if = "Option::is_none")]
670 #[is_empty(if = "is_empty::is_option_really_empty")]
671 pub if_cond: Option<Schema>,
672 #[serde(skip_serializing_if = "Option::is_none")]
675 #[builder(name = "then_cond")]
676 #[is_empty(if = "is_empty::is_option_really_empty")]
677 pub then: Option<Schema>,
678 #[serde(rename = "else", skip_serializing_if = "Option::is_none")]
681 #[is_empty(if = "is_empty::is_option_really_empty")]
682 pub else_cond: Option<Schema>,
683 #[serde(skip_serializing_if = "Option::is_none")]
686 #[is_empty(if = "is_empty::is_option_really_empty")]
687 pub not: Option<Schema>,
688 #[serde(rename = "unevaluatedItems", skip_serializing_if = "Option::is_none")]
691 #[is_empty(if = "is_empty::is_option_really_empty")]
692 pub unevaluated_items: Option<Schema>,
693 #[serde(rename = "unevaluatedProperties", skip_serializing_if = "Option::is_none")]
696 #[is_empty(if = "is_empty::is_option_really_empty")]
697 pub unevaluated_properties: Option<Schema>,
698 #[serde(skip_serializing_if = "Option::is_none")]
701 #[is_empty(if = "is_empty::is_option_really_empty")]
702 pub discriminator: Option<Discriminator>,
703 #[serde(flatten)]
705 #[is_empty(if = "is_empty::is_option_really_empty")]
706 pub extensions: Option<Extensions>,
707}
708
709impl From<Ref> for Object {
710 fn from(value: Ref) -> Self {
711 Self::builder()
712 .reference(value.ref_location)
713 .description(value.description)
714 .summary(value.summary)
715 .build()
716 }
717}
718
719impl<S: object_builder::State> ObjectBuilder<S> {
720 pub fn properties<P: Into<String>, C: Into<Schema>>(mut self, properties: impl IntoIterator<Item = (P, C)>) -> Self {
722 self.properties
723 .extend(properties.into_iter().map(|(p, s)| (p.into(), s.into())));
724 self
725 }
726
727 pub fn property(mut self, name: impl Into<String>, schema: impl Into<Schema>) -> Self {
729 self.properties.insert(name.into(), schema.into());
730 self
731 }
732
733 pub fn all_of(mut self, all_of: impl Into<Schema>) -> Self {
735 self.all_of.push(all_of.into());
736 self
737 }
738
739 pub fn all_ofs<C: Into<Schema>>(mut self, all_ofs: impl IntoIterator<Item = C>) -> Self {
741 self.all_of.extend(all_ofs.into_iter().map(|s| s.into()));
742 self
743 }
744
745 pub fn any_ofs<C: Into<Schema>>(self, any_ofs: impl IntoIterator<Item = C>) -> Self {
747 any_ofs.into_iter().fold(self, |this, c| this.any_of(c))
748 }
749
750 pub fn any_of(mut self, any_of: impl Into<Schema>) -> Self {
752 self.any_of.get_or_insert_default().push(any_of.into());
753 self
754 }
755
756 pub fn one_ofs<C: Into<Schema>>(self, one_ofs: impl IntoIterator<Item = C>) -> Self {
758 one_ofs.into_iter().fold(self, |this, c| this.one_of(c))
759 }
760
761 pub fn one_of(mut self, one_of: impl Into<Schema>) -> Self {
763 self.one_of.get_or_insert_default().push(one_of.into());
764 self
765 }
766
767 pub fn enum_value(mut self, enum_value: impl Into<serde_json::Value>) -> Self {
769 self.enum_values.get_or_insert_default().push(enum_value.into());
770 self
771 }
772
773 pub fn enum_values<E: Into<serde_json::Value>>(self, enum_values: impl IntoIterator<Item = E>) -> Self {
775 enum_values.into_iter().fold(self, |this, e| this.enum_value(e))
776 }
777
778 pub fn require(mut self, require: impl Into<String>) -> Self {
780 self.required.push(require.into());
781 self
782 }
783
784 pub fn required<R: Into<String>>(self, required: impl IntoIterator<Item = R>) -> Self {
786 required.into_iter().fold(self, |this, e| this.require(e))
787 }
788
789 pub fn example(mut self, example: impl Into<serde_json::Value>) -> Self {
791 self.examples.push(example.into());
792 self
793 }
794
795 pub fn examples<E: Into<serde_json::Value>>(self, examples: impl IntoIterator<Item = E>) -> Self {
797 examples.into_iter().fold(self, |this, e| this.example(e))
798 }
799}
800
801impl<S: object_builder::IsComplete> ObjectBuilder<S> {
802 pub fn to_array(self) -> ObjectBuilder<object_builder::SetItems<object_builder::SetSchemaType>> {
804 Object::builder().schema_type(Type::Array).items(self)
805 }
806}
807
808impl<S: object_builder::IsComplete> From<ObjectBuilder<S>> for Object {
809 fn from(value: ObjectBuilder<S>) -> Self {
810 value.build()
811 }
812}
813
814impl<S: object_builder::IsComplete> From<ObjectBuilder<S>> for Schema {
815 fn from(value: ObjectBuilder<S>) -> Self {
816 value.build().into()
817 }
818}
819
820impl Object {
821 pub fn with_type(ty: impl Into<Types>) -> ObjectBuilder<object_builder::SetSchemaType> {
831 Object::builder().schema_type(ty)
832 }
833
834 pub fn int32() -> Object {
836 Object::builder()
837 .schema_type(Type::Integer)
838 .maximum(i32::MAX as f64)
839 .minimum(i32::MIN as f64)
840 .build()
841 }
842
843 pub fn int64() -> Object {
845 Object::builder()
846 .schema_type(Type::Integer)
847 .maximum(i64::MAX as f64)
848 .minimum(i64::MIN as f64)
849 .build()
850 }
851
852 pub fn uint32() -> Object {
854 Object::builder()
855 .schema_type(Type::Integer)
856 .maximum(u32::MAX as f64)
857 .minimum(u32::MIN as f64)
858 .build()
859 }
860
861 pub fn uint64() -> Object {
863 Object::builder()
864 .schema_type(Type::Integer)
865 .maximum(u64::MAX as f64)
866 .minimum(u64::MIN as f64)
867 .build()
868 }
869
870 pub fn to_array(self) -> Self {
872 Self::builder().schema_type(Type::Array).items(self).build()
873 }
874
875 pub fn all_ofs<S: Into<Schema>>(all_ofs: impl IntoIterator<Item = S>) -> Object {
885 Object::builder().all_ofs(all_ofs).build()
886 }
887}
888
889macro_rules! iter_chain {
890 ($($item:expr),*$(,)?) => {
891 std::iter::empty()
892 $(.chain($item))*
893 };
894}
895
896macro_rules! merge_item {
897 ([$self:ident, $other:ident] => { $($item:ident => $merge_behaviour:expr),*$(,)? }) => {$({
898 let self_item = &mut $self.$item;
899 let other_item = &mut $other.$item;
900 if self_item.is_empty() {
901 *self_item = std::mem::take(other_item);
902 } else if self_item == other_item {
903 std::mem::take(other_item);
904 } else if !other_item.is_empty() {
905 $merge_behaviour(self_item, other_item);
906 }
907 })*};
908}
909
910fn dedupe_array<T: PartialEq>(items: &mut Vec<T>) {
911 let mut dedupe = Vec::new();
912 for item in items.drain(..) {
913 if !dedupe.contains(&item) {
914 dedupe.push(item);
915 }
916 }
917
918 *items = dedupe;
919}
920
921impl Object {
922 pub fn optimize(&mut self) {
925 let mut all_ofs = Vec::new();
927 self.take_all_ofs(&mut all_ofs);
928
929 all_ofs
930 .iter_mut()
931 .filter_map(|schema| schema.as_object_mut())
932 .for_each(|schema| self.merge(schema));
933
934 let sub_schemas = iter_chain!(
936 self.schema.iter_mut(),
937 self.additional_items.iter_mut(),
938 self.contains.iter_mut(),
939 self.additional_properties.iter_mut(),
940 self.items.iter_mut(),
941 self.prefix_items.iter_mut().flatten(),
942 self.definitions.values_mut(),
943 self.properties.values_mut(),
944 self.pattern_properties.values_mut(),
945 self.dependencies.values_mut(),
946 self.property_names.iter_mut(),
947 self.if_cond.iter_mut(),
948 self.then.iter_mut(),
949 self.else_cond.iter_mut(),
950 self.any_of.iter_mut().flatten(),
951 self.one_of.iter_mut().flatten(),
952 self.not.iter_mut(),
953 self.unevaluated_items.iter_mut(),
954 self.unevaluated_properties.iter_mut(),
955 self.content_schema.iter_mut(),
956 );
957
958 for schema in sub_schemas {
959 schema.optimize();
960 }
961
962 self.all_of = all_ofs.into_iter().filter(|schema| !schema.is_empty()).collect();
963 dedupe_array(&mut self.examples);
964 dedupe_array(&mut self.required);
965 if let Some(_enum) = &mut self.enum_values {
966 dedupe_array(_enum);
967 }
968 dedupe_array(&mut self.all_of);
969 if let Some(any_of) = &mut self.any_of {
970 dedupe_array(any_of);
971 }
972 if let Some(one_of) = &mut self.one_of {
973 dedupe_array(one_of);
974 }
975 }
976
977 pub fn into_optimized(mut self) -> Self {
979 self.optimize();
980 self
981 }
982
983 fn take_all_ofs(&mut self, collection: &mut Vec<Schema>) {
984 for mut schema in self.all_of.drain(..) {
985 schema.take_all_ofs(collection);
986 collection.push(schema);
987 }
988 }
989
990 fn merge(&mut self, other: &mut Self) {
991 merge_item!(
992 [self, other] => {
993 id => merge_skip,
994 schema => merge_sub_schema,
995 reference => merge_skip,
996 comment => merge_drop_second,
997 title => merge_drop_second,
998 description => merge_drop_second,
999 summary => merge_drop_second,
1000 default => merge_drop_second,
1001 read_only => merge_set_true,
1002 examples => merge_array_combine,
1003 multiple_of => merge_multiple_of,
1004 maximum => merge_min,
1005 exclusive_maximum => merge_min,
1006 minimum => merge_max,
1007 exclusive_minimum => merge_min,
1008 max_length => merge_min,
1009 min_length => merge_max,
1010 pattern => merge_skip,
1011 additional_items => merge_sub_schema,
1012 items => merge_sub_schema,
1013 prefix_items => merge_prefix_items,
1014 max_items => merge_min,
1015 min_items => merge_max,
1016 unique_items => merge_set_true,
1017 contains => merge_sub_schema,
1018 max_properties => merge_min,
1019 min_properties => merge_max,
1020 max_contains => merge_min,
1021 min_contains => merge_max,
1022 required => merge_array_combine,
1023 additional_properties => merge_sub_schema,
1024 definitions => merge_schema_map,
1025 properties => merge_schema_map,
1026 pattern_properties => merge_schema_map,
1027 dependencies => merge_schema_map,
1028 property_names => merge_sub_schema,
1029 const_value => merge_skip,
1030 enum_values => merge_array_union_optional,
1031 schema_type => merge_type,
1032 format => merge_skip,
1033 content_media_type => merge_skip,
1034 content_encoding => merge_skip,
1035 any_of => merge_array_combine_optional,
1039 one_of => merge_array_combine_optional,
1040 not => merge_inverted_if_possible,
1041 unevaluated_items => merge_sub_schema,
1042 unevaluated_properties => merge_sub_schema,
1043 deprecated => merge_set_true,
1044 write_only => merge_set_true,
1045 content_schema => merge_sub_schema,
1046 }
1047 );
1048 }
1049}
1050
1051fn merge_skip<T>(_: &mut T, _: &mut T) {}
1052
1053fn merge_drop_second<T: Default>(_: &mut T, other: &mut T) {
1054 std::mem::take(other);
1055}
1056
1057fn merge_min<T: Ord + Copy>(value: &mut Option<T>, other: &mut Option<T>) {
1058 let value = value.as_mut().unwrap();
1059 let other = other.take().unwrap();
1060 *value = (*value).min(other);
1061}
1062
1063fn merge_max<T: Ord + Copy>(value: &mut Option<T>, other: &mut Option<T>) {
1064 let value = value.as_mut().unwrap();
1065 let other = other.take().unwrap();
1066 *value = (*value).max(other);
1067}
1068
1069fn merge_set_true(value: &mut Option<bool>, other: &mut Option<bool>) {
1070 other.take();
1071 value.replace(true);
1072}
1073
1074fn merge_sub_schema(value: &mut Option<Schema>, other_opt: &mut Option<Schema>) {
1075 let value = value.as_mut().unwrap();
1076 let mut other = other_opt.take().unwrap();
1077 value.merge(&mut other);
1078 if !other.is_empty() {
1079 other_opt.replace(other);
1080 }
1081}
1082
1083fn merge_inverted_if_possible(value_opt: &mut Option<Schema>, other_opt: &mut Option<Schema>) {
1084 let value = value_opt.as_ref().unwrap();
1093 let other = other_opt.as_ref().unwrap();
1094 if let (Schema::Object(value_obj), Schema::Object(other_obj)) = (value, other) {
1095 let mut self_copy = (*value_obj).clone();
1096 let mut other_copy = (*other_obj).clone();
1097 {
1099 merge_item!(
1100 [self_copy, other_copy] => {
1101 id => merge_skip,
1102 schema => merge_skip,
1103 reference => merge_skip,
1104 comment => merge_skip,
1105 title => merge_skip,
1106 description => merge_skip,
1107 summary => merge_skip,
1108 default => merge_skip,
1109 read_only => merge_skip,
1110 examples => merge_skip,
1111 multiple_of => merge_skip,
1112 maximum => merge_max,
1113 exclusive_maximum => merge_max,
1114 minimum => merge_min,
1115 exclusive_minimum => merge_max,
1116 max_length => merge_max,
1117 min_length => merge_min,
1118 pattern => merge_skip,
1119 additional_items => merge_skip,
1120 items => merge_skip,
1121 prefix_items => merge_skip,
1122 max_items => merge_max,
1123 min_items => merge_min,
1124 unique_items => merge_skip,
1125 contains => merge_skip,
1126 max_properties => merge_max,
1127 min_properties => merge_min,
1128 max_contains => merge_max,
1129 min_contains => merge_min,
1130 required => merge_skip,
1131 additional_properties => merge_skip,
1132 definitions => merge_skip,
1133 properties => merge_skip,
1134 pattern_properties => merge_skip,
1135 dependencies => merge_skip,
1136 property_names => merge_skip,
1137 const_value => merge_skip,
1138 enum_values => merge_array_combine_optional,
1139 schema_type => merge_skip,
1140 format => merge_skip,
1141 content_media_type => merge_skip,
1142 content_encoding => merge_skip,
1143 any_of => merge_array_combine_optional,
1147 one_of => merge_array_combine_optional,
1148 not => merge_skip,
1149 unevaluated_items => merge_skip,
1150 unevaluated_properties => merge_skip,
1151 deprecated => merge_skip,
1152 write_only => merge_skip,
1153 content_schema => merge_skip,
1154 }
1155 );
1156 }
1157
1158 if other_copy.const_value.is_some() {
1160 let mut disallowed = self_copy.enum_values.unwrap_or_default();
1161 disallowed.push(other_copy.const_value.unwrap());
1162 other_copy.const_value = None;
1163 if self_copy.const_value.is_some() {
1164 disallowed.push(self_copy.const_value.unwrap());
1165 self_copy.const_value = None;
1166 }
1167 disallowed.dedup();
1168 self_copy.enum_values = Some(disallowed);
1169 }
1170
1171 if other_copy.is_empty() {
1173 value_opt.replace(Schema::Object(self_copy));
1174 *other_opt = Default::default();
1175 }
1176 }
1177}
1178
1179fn merge_array_combine<T: PartialEq>(value: &mut Vec<T>, other: &mut Vec<T>) {
1180 value.append(other);
1181}
1182
1183fn merge_array_union<T: PartialEq>(value: &mut Vec<T>, other: &mut Vec<T>) {
1184 let other = std::mem::take(other);
1185 value.retain(|v| other.contains(v));
1186}
1187
1188fn merge_array_union_optional<T: PartialEq>(value: &mut Option<Vec<T>>, other: &mut Option<Vec<T>>) {
1189 merge_array_union(value.as_mut().unwrap(), other.as_mut().unwrap());
1190 if other.as_ref().is_some_and(|o| o.is_empty()) {
1191 other.take();
1192 }
1193}
1194
1195fn merge_array_combine_optional<T: PartialEq>(value: &mut Option<Vec<T>>, other: &mut Option<Vec<T>>) {
1196 merge_array_combine(value.as_mut().unwrap(), other.as_mut().unwrap());
1197 if other.as_ref().is_some_and(|o| o.is_empty()) {
1198 other.take();
1199 }
1200}
1201
1202fn merge_schema_map(value: &mut IndexMap<String, Schema>, other: &mut IndexMap<String, Schema>) {
1203 for (key, mut other) in other.drain(..) {
1204 match value.entry(key) {
1205 indexmap::map::Entry::Occupied(mut value) => {
1206 value.get_mut().merge(&mut other);
1207 if !other.is_empty()
1208 && let Some(obj) = value.get_mut().as_object_mut()
1209 {
1210 obj.all_of.push(other);
1211 }
1212 }
1213 indexmap::map::Entry::Vacant(v) => {
1214 v.insert(other);
1215 }
1216 }
1217 }
1218}
1219
1220fn merge_type(value: &mut Option<Types>, other: &mut Option<Types>) {
1221 match (value.as_mut().unwrap(), other.take().unwrap()) {
1222 (Types::Single(s), Types::Single(ref o)) if s != o => {
1223 value.replace(Types::Multi(Vec::new()));
1224 }
1225 (Types::Single(_), Types::Single(_)) => {}
1226 (Types::Multi(s), Types::Multi(ref mut o)) => {
1227 merge_array_union(s, o);
1228 }
1229 (&mut Types::Single(s), Types::Multi(ref o)) | (&mut Types::Multi(ref o), Types::Single(s)) => {
1230 if o.contains(&s) {
1231 value.replace(Types::Single(s));
1232 } else {
1233 value.replace(Types::Multi(Vec::new()));
1234 }
1235 }
1236 }
1237}
1238
1239fn merge_prefix_items(value: &mut Option<Vec<Schema>>, other: &mut Option<Vec<Schema>>) {
1240 let mut other = other.take().unwrap_or_default();
1241 let value = value.as_mut().unwrap();
1242 value.extend(other.drain(value.len()..));
1243 for (value, mut other) in value.iter_mut().zip(other) {
1244 value.merge(&mut other);
1245 if !other.is_empty()
1246 && let Some(obj) = value.as_object_mut()
1247 {
1248 obj.all_of.push(other);
1249 }
1250 }
1251}
1252
1253fn merge_multiple_of(value: &mut Option<OrderedFloat<f64>>, other: &mut Option<OrderedFloat<f64>>) {
1254 let value = value.as_mut().unwrap().as_mut();
1255 let other = other.take().unwrap().into_inner();
1256
1257 fn gcd_f64(mut a: f64, mut b: f64) -> f64 {
1258 a = a.abs();
1259 b = b.abs();
1260 if a == 0.0 {
1262 return b;
1263 }
1264 if b == 0.0 {
1265 return a;
1266 }
1267 while b > 0.0 {
1269 let r = a % b;
1270 a = b;
1271 b = r;
1272 }
1273 a
1274 }
1275
1276 fn lcm_f64(a: f64, b: f64) -> f64 {
1278 if a == 0.0 || b == 0.0 {
1279 return 0.0;
1280 }
1281 let g = gcd_f64(a, b);
1282 (a / g * b).abs()
1284 }
1285
1286 *value = lcm_f64(*value, other);
1287}
1288
1289#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq)]
1291#[cfg_attr(feature = "debug", derive(Debug))]
1292#[serde(untagged)]
1293#[non_exhaustive]
1294pub enum Schema {
1295 Object(Box<Object>),
1297 Bool(bool),
1299}
1300
1301impl From<Object> for Schema {
1302 fn from(value: Object) -> Self {
1303 Self::object(value)
1304 }
1305}
1306
1307impl From<bool> for Schema {
1308 fn from(value: bool) -> Self {
1309 Self::Bool(value)
1310 }
1311}
1312
1313impl IsEmpty for Schema {
1314 fn is_empty(&self) -> bool {
1315 match self {
1316 Self::Bool(result) => *result,
1317 Self::Object(obj) => obj.is_empty(),
1318 }
1319 }
1320}
1321
1322impl Schema {
1323 pub fn to_array(self) -> Self {
1325 Self::object(Object::builder().schema_type(Type::Array).items(self))
1326 }
1327
1328 pub fn optimize(&mut self) {
1330 match self {
1331 Self::Bool(_) => {}
1332 Self::Object(obj) => obj.optimize(),
1333 }
1334 }
1335
1336 pub fn into_optimized(mut self) -> Self {
1338 match &mut self {
1339 Self::Bool(_) => {}
1340 Self::Object(obj) => obj.optimize(),
1341 }
1342 self
1343 }
1344
1345 pub fn object(value: impl Into<Object>) -> Self {
1347 Self::Object(value.into().into())
1348 }
1349
1350 fn take_all_ofs(&mut self, collection: &mut Vec<Schema>) {
1351 match self {
1352 Self::Bool(_) => {}
1353 Self::Object(obj) => obj.take_all_ofs(collection),
1354 }
1355 }
1356
1357 fn as_object_mut(&mut self) -> Option<&mut Object> {
1358 match self {
1359 Self::Bool(_) => None,
1360 Self::Object(obj) => Some(obj.as_mut()),
1361 }
1362 }
1363
1364 fn merge(&mut self, other: &mut Self) {
1365 match (self, other) {
1366 (this @ Schema::Bool(false), _) | (this, Schema::Bool(false)) => {
1367 *this = Schema::Bool(false);
1368 }
1369 (this @ Schema::Bool(true), other) => {
1370 std::mem::swap(this, other);
1371 }
1372 (_, Schema::Bool(true)) => {}
1373 (Schema::Object(value), Schema::Object(other)) => {
1374 value.merge(other.as_mut());
1375 }
1376 }
1377 }
1378}
1379
1380#[cfg(test)]
1381#[cfg_attr(coverage_nightly, coverage(off))]
1382mod tests {
1383 use insta::assert_json_snapshot;
1384 use serde_json::{Value, json};
1385
1386 use super::*;
1387 use crate::*;
1388
1389 #[test]
1390 fn create_schema_serializes_json() -> Result<(), serde_json::Error> {
1391 let openapi = OpenApi::builder()
1392 .info(Info::new("My api", "1.0.0"))
1393 .paths(Paths::new())
1394 .components(
1395 Components::builder()
1396 .schema("Person", Ref::new("#/components/PersonModel"))
1397 .schema(
1398 "Credential",
1399 Schema::from(
1400 Object::builder()
1401 .property(
1402 "id",
1403 Object::builder()
1404 .schema_type(Type::Integer)
1405 .format("int32")
1406 .description("Id of credential")
1407 .default(1i32),
1408 )
1409 .property(
1410 "name",
1411 Object::builder().schema_type(Type::String).description("Name of credential"),
1412 )
1413 .property(
1414 "status",
1415 Object::builder()
1416 .schema_type(Type::String)
1417 .default("Active")
1418 .description("Credential status")
1419 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1420 )
1421 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1422 .property("tags", Object::builder().schema_type(Type::String).build().to_array()),
1423 ),
1424 )
1425 .build(),
1426 )
1427 .build();
1428
1429 let serialized = serde_json::to_string_pretty(&openapi)?;
1430 println!("serialized json:\n {serialized}");
1431
1432 let value = serde_json::to_value(&openapi)?;
1433 let credential = get_json_path(&value, "components.schemas.Credential.properties");
1434 let person = get_json_path(&value, "components.schemas.Person");
1435
1436 assert!(
1437 credential.get("id").is_some(),
1438 "could not find path: components.schemas.Credential.properties.id"
1439 );
1440 assert!(
1441 credential.get("status").is_some(),
1442 "could not find path: components.schemas.Credential.properties.status"
1443 );
1444 assert!(
1445 credential.get("name").is_some(),
1446 "could not find path: components.schemas.Credential.properties.name"
1447 );
1448 assert!(
1449 credential.get("history").is_some(),
1450 "could not find path: components.schemas.Credential.properties.history"
1451 );
1452
1453 let id = credential.get("id").unwrap().as_object().unwrap();
1454 assert_eq!(
1455 id.get("default").unwrap().as_number().unwrap().as_i64().unwrap(),
1456 1,
1457 "components.schemas.Credential.properties.id.default did not match"
1458 );
1459 assert_eq!(
1460 id.get("description").unwrap().as_str().unwrap(),
1461 "Id of credential",
1462 "components.schemas.Credential.properties.id.description did not match"
1463 );
1464 assert_eq!(
1465 id.get("format").unwrap().as_str().unwrap(),
1466 "int32",
1467 "components.schemas.Credential.properties.id.format did not match"
1468 );
1469 assert_eq!(
1470 id.get("type").unwrap().as_str().unwrap(),
1471 "integer",
1472 "components.schemas.Credential.properties.id.type did not match"
1473 );
1474
1475 let name = credential.get("name").unwrap().as_object().unwrap();
1476 assert_eq!(
1477 name.get("description").unwrap().as_str().unwrap(),
1478 "Name of credential",
1479 "components.schemas.Credential.properties.name.description did not match"
1480 );
1481 assert_eq!(
1482 name.get("type").unwrap().as_str().unwrap(),
1483 "string",
1484 "components.schemas.Credential.properties.name.type did not match"
1485 );
1486
1487 let status = credential.get("status").unwrap().as_object().unwrap();
1488 assert_eq!(
1489 status.get("default").unwrap().as_str().unwrap(),
1490 "Active",
1491 "components.schemas.Credential.properties.status.default did not match"
1492 );
1493 assert_eq!(
1494 status.get("description").unwrap().as_str().unwrap(),
1495 "Credential status",
1496 "components.schemas.Credential.properties.status.description did not match"
1497 );
1498 assert_eq!(
1499 status.get("enum").unwrap().to_string(),
1500 r#"["Active","NotActive","Locked","Expired"]"#,
1501 "components.schemas.Credential.properties.status.enum did not match"
1502 );
1503 assert_eq!(
1504 status.get("type").unwrap().as_str().unwrap(),
1505 "string",
1506 "components.schemas.Credential.properties.status.type did not match"
1507 );
1508
1509 let history = credential.get("history").unwrap().as_object().unwrap();
1510 assert_eq!(
1511 history.get("items").unwrap().to_string(),
1512 r###"{"$ref":"#/components/schemas/UpdateHistory"}"###,
1513 "components.schemas.Credential.properties.history.items did not match"
1514 );
1515 assert_eq!(
1516 history.get("type").unwrap().as_str().unwrap(),
1517 "array",
1518 "components.schemas.Credential.properties.history.type did not match"
1519 );
1520
1521 assert_eq!(
1522 person.to_string(),
1523 r###"{"$ref":"#/components/PersonModel"}"###,
1524 "components.schemas.Person.ref did not match"
1525 );
1526
1527 Ok(())
1528 }
1529
1530 #[test]
1532 fn test_property_order() {
1533 let json_value = Object::builder()
1534 .property(
1535 "id",
1536 Object::builder()
1537 .schema_type(Type::Integer)
1538 .format("int32")
1539 .description("Id of credential")
1540 .default(1i32),
1541 )
1542 .property(
1543 "name",
1544 Object::builder().schema_type(Type::String).description("Name of credential"),
1545 )
1546 .property(
1547 "status",
1548 Object::builder()
1549 .schema_type(Type::String)
1550 .default("Active")
1551 .description("Credential status")
1552 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1553 )
1554 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1555 .property("tags", Object::builder().schema_type(Type::String).to_array())
1556 .build();
1557
1558 assert_eq!(
1559 json_value.properties.keys().collect::<Vec<_>>(),
1560 vec!["id", "name", "status", "history", "tags"]
1561 );
1562 }
1563
1564 #[test]
1566 fn test_additional_properties() {
1567 let json_value = Object::builder()
1568 .schema_type(Type::Object)
1569 .additional_properties(Object::builder().schema_type(Type::String))
1570 .build();
1571 assert_json_snapshot!(json_value, @r#"
1572 {
1573 "additionalProperties": {
1574 "type": "string"
1575 },
1576 "type": "object"
1577 }
1578 "#);
1579
1580 let json_value = Object::builder()
1581 .schema_type(Type::Object)
1582 .additional_properties(Object::builder().schema_type(Type::Number).to_array())
1583 .build();
1584
1585 assert_json_snapshot!(json_value, @r#"
1586 {
1587 "additionalProperties": {
1588 "items": {
1589 "type": "number"
1590 },
1591 "type": "array"
1592 },
1593 "type": "object"
1594 }
1595 "#);
1596
1597 let json_value = Object::builder()
1598 .schema_type(Type::Object)
1599 .additional_properties(Ref::from_schema_name("ComplexModel"))
1600 .build();
1601 assert_json_snapshot!(json_value, @r##"
1602 {
1603 "additionalProperties": {
1604 "$ref": "#/components/schemas/ComplexModel"
1605 },
1606 "type": "object"
1607 }
1608 "##);
1609 }
1610
1611 #[test]
1612 fn test_object_with_title() {
1613 let json_value = Object::builder().schema_type(Type::Object).title("SomeName").build();
1614 assert_json_snapshot!(json_value, @r#"
1615 {
1616 "title": "SomeName",
1617 "type": "object"
1618 }
1619 "#);
1620 }
1621
1622 #[test]
1623 fn derive_object_with_examples() {
1624 let json_value = Object::builder()
1625 .schema_type(Type::Object)
1626 .examples([json!({"age": 20, "name": "bob the cat"})])
1627 .build();
1628 assert_json_snapshot!(json_value, @r#"
1629 {
1630 "examples": [
1631 {
1632 "age": 20,
1633 "name": "bob the cat"
1634 }
1635 ],
1636 "type": "object"
1637 }
1638 "#);
1639 }
1640
1641 fn get_json_path<'a>(value: &'a Value, path: &str) -> &'a Value {
1642 path.split('.').fold(value, |acc, fragment| {
1643 acc.get(fragment).unwrap_or(&serde_json::value::Value::Null)
1644 })
1645 }
1646
1647 #[test]
1648 fn test_array_new() {
1649 let array = Object::builder()
1650 .property(
1651 "id",
1652 Object::builder()
1653 .schema_type(Type::Integer)
1654 .format("int32")
1655 .description("Id of credential")
1656 .default(json!(1i32)),
1657 )
1658 .to_array()
1659 .build();
1660
1661 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1662 }
1663
1664 #[test]
1665 fn test_array_builder() {
1666 let array = Object::builder()
1667 .schema_type(Type::Array)
1668 .items(
1669 Object::builder().property(
1670 "id",
1671 Object::builder()
1672 .schema_type(Type::Integer)
1673 .format("int32")
1674 .description("Id of credential")
1675 .default(1i32),
1676 ),
1677 )
1678 .build();
1679
1680 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1681 }
1682
1683 #[test]
1684 fn reserialize_deserialized_schema_components() {
1685 let components = Components::builder()
1686 .schemas_from_iter([(
1687 "Comp",
1688 Schema::from(
1689 Object::builder()
1690 .property("name", Object::builder().schema_type(Type::String))
1691 .required(["name"]),
1692 ),
1693 )])
1694 .responses_from_iter(vec![("200", Response::builder().description("Okay").build())])
1695 .security_scheme(
1696 "TLS",
1697 SecurityScheme::MutualTls {
1698 description: None,
1699 extensions: None,
1700 },
1701 )
1702 .build();
1703
1704 let serialized_components = serde_json::to_string(&components).unwrap();
1705
1706 let deserialized_components: Components = serde_json::from_str(serialized_components.as_str()).unwrap();
1707
1708 assert_eq!(
1709 serialized_components,
1710 serde_json::to_string(&deserialized_components).unwrap()
1711 )
1712 }
1713
1714 #[test]
1715 fn reserialize_deserialized_object_component() {
1716 let prop = Object::builder()
1717 .property("name", Object::builder().schema_type(Type::String))
1718 .required(["name"])
1719 .build();
1720
1721 let serialized_components = serde_json::to_string(&prop).unwrap();
1722 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1723
1724 assert_eq!(
1725 serialized_components,
1726 serde_json::to_string(&deserialized_components).unwrap()
1727 )
1728 }
1729
1730 #[test]
1731 fn reserialize_deserialized_property() {
1732 let prop = Object::builder().schema_type(Type::String).build();
1733
1734 let serialized_components = serde_json::to_string(&prop).unwrap();
1735 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1736
1737 assert_eq!(
1738 serialized_components,
1739 serde_json::to_string(&deserialized_components).unwrap()
1740 )
1741 }
1742
1743 #[test]
1744 fn deserialize_reserialize_one_of_default_type() {
1745 let a = Object::builder()
1746 .one_ofs([
1747 Object::builder().property("element", Ref::new("#/test")),
1748 Object::builder().property("foobar", Ref::new("#/foobar")),
1749 ])
1750 .build();
1751
1752 let serialized_json = serde_json::to_string(&a).expect("should serialize to json");
1753 let b: Object = serde_json::from_str(&serialized_json).expect("should deserialize OneOf");
1754 let reserialized_json = serde_json::to_string(&b).expect("reserialized json");
1755
1756 println!("{serialized_json}");
1757 println!("{reserialized_json}",);
1758 assert_eq!(serialized_json, reserialized_json);
1759 }
1760
1761 #[test]
1762 fn serialize_deserialize_any_of_of_within_ref_or_t_object_builder() {
1763 let ref_or_schema = Object::builder()
1764 .property(
1765 "test",
1766 Object::builder()
1767 .any_ofs([
1768 Object::builder().property("element", Ref::new("#/test")).build().to_array(),
1769 Object::builder().property("foobar", Ref::new("#/foobar")).build(),
1770 ])
1771 .build(),
1772 )
1773 .build();
1774
1775 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1776 println!("----------------------------");
1777 println!("{json_str}");
1778
1779 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1780
1781 let json_de_str = serde_json::to_string(&deserialized).expect("");
1782 println!("----------------------------");
1783 println!("{json_de_str}");
1784 assert!(json_str.contains("\"anyOf\""));
1785 assert_eq!(json_str, json_de_str);
1786 }
1787
1788 #[test]
1789 fn serialize_deserialize_schema_array_ref_or_t() {
1790 let ref_or_schema = Object::builder()
1791 .property("element", Ref::new("#/test"))
1792 .to_array()
1793 .to_array()
1794 .build();
1795
1796 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1797 println!("----------------------------");
1798 println!("{json_str}");
1799
1800 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1801
1802 let json_de_str = serde_json::to_string(&deserialized).expect("");
1803 println!("----------------------------");
1804 println!("{json_de_str}");
1805
1806 assert_eq!(json_str, json_de_str);
1807 }
1808
1809 #[test]
1810 fn serialize_deserialize_schema_array_builder() {
1811 let ref_or_schema = Object::builder().property("element", Ref::new("#/test")).build().to_array();
1812
1813 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1814 println!("----------------------------");
1815 println!("{json_str}");
1816
1817 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1818
1819 let json_de_str = serde_json::to_string(&deserialized).expect("");
1820 println!("----------------------------");
1821 println!("{json_de_str}");
1822
1823 assert_eq!(json_str, json_de_str);
1824 }
1825
1826 #[test]
1827 fn serialize_deserialize_schema_with_additional_properties() {
1828 let schema = Object::builder()
1829 .property("map", Object::builder().additional_properties(true))
1830 .build();
1831
1832 let json_str = serde_json::to_string(&schema).unwrap();
1833 println!("----------------------------");
1834 println!("{json_str}");
1835
1836 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1837
1838 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1839 println!("----------------------------");
1840 println!("{json_de_str}");
1841
1842 assert_eq!(json_str, json_de_str);
1843 }
1844
1845 #[test]
1846 fn serialize_deserialize_schema_with_additional_properties_object() {
1847 let schema = Object::builder()
1848 .property(
1849 "map",
1850 Object::builder()
1851 .additional_properties(Object::builder().property("name", Object::builder().schema_type(Type::String))),
1852 )
1853 .build();
1854
1855 let json_str = serde_json::to_string(&schema).unwrap();
1856 println!("----------------------------");
1857 println!("{json_str}");
1858
1859 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1860
1861 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1862 println!("----------------------------");
1863 println!("{json_de_str}");
1864
1865 assert_eq!(json_str, json_de_str);
1866 }
1867
1868 #[test]
1869 fn serialize_discriminator_with_mapping() {
1870 let mut discriminator = Discriminator::new("type");
1871 discriminator.mapping = [("int".to_string(), "#/components/schemas/MyInt".to_string())]
1872 .into_iter()
1873 .collect::<IndexMap<_, _>>();
1874 let one_of = Object::builder()
1875 .one_of(Ref::from_schema_name("MyInt"))
1876 .discriminator(discriminator)
1877 .build();
1878 assert_json_snapshot!(one_of, @r##"
1879 {
1880 "oneOf": [
1881 {
1882 "$ref": "#/components/schemas/MyInt"
1883 }
1884 ],
1885 "discriminator": {
1886 "propertyName": "type",
1887 "mapping": {
1888 "int": "#/components/schemas/MyInt"
1889 }
1890 }
1891 }
1892 "##);
1893 }
1894
1895 #[test]
1896 fn serialize_deserialize_object_with_multiple_schema_types() {
1897 let object = Object::builder().schema_type(vec![Type::Object, Type::Null]).build();
1898
1899 let json_str = serde_json::to_string(&object).unwrap();
1900 println!("----------------------------");
1901 println!("{json_str}");
1902
1903 let deserialized: Object = serde_json::from_str(&json_str).unwrap();
1904
1905 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1906 println!("----------------------------");
1907 println!("{json_de_str}");
1908
1909 assert_eq!(json_str, json_de_str);
1910 }
1911
1912 #[test]
1913 fn object_with_extensions() {
1914 let expected = json!("value");
1915 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1916 let json_value = Object::builder().extensions(extensions).build();
1917
1918 let value = serde_json::to_value(&json_value).unwrap();
1919 assert_eq!(value.get("x-some-extension"), Some(&expected));
1920 }
1921
1922 #[test]
1923 fn array_with_extensions() {
1924 let expected = json!("value");
1925 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1926 let json_value = Object::builder().extensions(extensions).to_array().build();
1927
1928 let value = serde_json::to_value(&json_value).unwrap();
1929 assert_eq!(value["items"].get("x-some-extension"), Some(&expected));
1930 }
1931
1932 #[test]
1933 fn oneof_with_extensions() {
1934 let expected = json!("value");
1935 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1936 let json_value = Object::builder()
1937 .one_of(Object::builder().extensions(extensions).build())
1938 .build();
1939
1940 let value = serde_json::to_value(&json_value).unwrap();
1941 assert_eq!(value["oneOf"][0].get("x-some-extension"), Some(&expected));
1942 }
1943
1944 #[test]
1945 fn allof_with_extensions() {
1946 let expected = json!("value");
1947 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1948 let json_value = Object::builder()
1949 .all_of(Object::builder().extensions(extensions).build())
1950 .build();
1951
1952 let value = serde_json::to_value(&json_value).unwrap();
1953 assert_eq!(value["allOf"][0].get("x-some-extension"), Some(&expected));
1954 }
1955
1956 #[test]
1957 fn anyof_with_extensions() {
1958 let expected = json!("value");
1959 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1960 let json_value = Object::builder()
1961 .any_of(Object::builder().extensions(extensions).build())
1962 .build();
1963
1964 let value = serde_json::to_value(&json_value).unwrap();
1965 assert_eq!(value["anyOf"][0].get("x-some-extension"), Some(&expected));
1966 }
1967
1968 #[test]
1969 fn merge_objects_with_not_enum_values() {
1970 let main_obj = Schema::object(
1971 Object::builder()
1972 .one_ofs([
1973 Schema::object(Object::builder().schema_type(Type::Number).build()),
1974 Schema::object(
1975 Object::builder()
1976 .schema_type(Type::String)
1977 .enum_values(vec![
1978 serde_json::Value::from("Infinity"),
1979 serde_json::Value::from("-Infinity"),
1980 serde_json::Value::from("NaN"),
1981 ])
1982 .build(),
1983 ),
1984 ])
1985 .build(),
1986 );
1987
1988 let not_nan = Schema::object(
1989 Object::builder()
1990 .not(Schema::object(
1991 Object::builder()
1992 .schema_type(Type::String)
1993 .enum_values(vec![serde_json::Value::from("NaN")])
1994 .build(),
1995 ))
1996 .build(),
1997 );
1998
1999 let not_infinity = Schema::object(
2000 Object::builder()
2001 .not(Schema::object(
2002 Object::builder()
2003 .schema_type(Type::String)
2004 .enum_values(vec![serde_json::Value::from("Infinity")])
2005 .build(),
2006 ))
2007 .build(),
2008 );
2009
2010 let schemas = vec![main_obj, not_nan, not_infinity];
2011 let merged = Object::all_ofs(schemas).into_optimized();
2012
2013 assert_json_snapshot!(merged, @r#"
2014 {
2015 "oneOf": [
2016 {
2017 "type": "number"
2018 },
2019 {
2020 "enum": [
2021 "Infinity",
2022 "-Infinity",
2023 "NaN"
2024 ],
2025 "type": "string"
2026 }
2027 ],
2028 "not": {
2029 "enum": [
2030 "NaN",
2031 "Infinity"
2032 ],
2033 "type": "string"
2034 }
2035 }
2036 "#);
2037 }
2038
2039 #[test]
2040 fn merge_objects_with_not_consts() {
2041 let not_a = Schema::object(
2042 Object::builder()
2043 .not(Schema::object(
2044 Object::builder()
2045 .schema_type(Type::String)
2046 .const_value(serde_json::Value::from("A"))
2047 .build(),
2048 ))
2049 .build(),
2050 );
2051
2052 let not_b = Schema::object(
2053 Object::builder()
2054 .not(Schema::object(
2055 Object::builder()
2056 .schema_type(Type::String)
2057 .const_value(serde_json::Value::from("B"))
2058 .build(),
2059 ))
2060 .build(),
2061 );
2062
2063 let schemas = vec![not_a, not_b];
2064 let merged = Object::all_ofs(schemas).into_optimized();
2065
2066 assert_json_snapshot!(merged, @r#"
2067 {
2068 "not": {
2069 "enum": [
2070 "B",
2071 "A"
2072 ],
2073 "type": "string"
2074 }
2075 }
2076 "#);
2077 }
2078
2079 #[test]
2080 fn dont_merge_objects_with_not_if_impossible() {
2081 let not_format_a = Schema::object(
2082 Object::builder()
2083 .not(Schema::object(
2084 Object::builder().schema_type(Type::String).format("email").build(),
2085 ))
2086 .build(),
2087 );
2088
2089 let not_format_b = Schema::object(
2090 Object::builder()
2091 .not(Schema::object(
2092 Object::builder().schema_type(Type::String).format("date-time").build(),
2093 ))
2094 .build(),
2095 );
2096
2097 let not_format_c = Schema::object(
2098 Object::builder()
2099 .not(Schema::object(
2100 Object::builder().schema_type(Type::String).format("ipv4").build(),
2101 ))
2102 .build(),
2103 );
2104
2105 let schemas = vec![not_format_a, not_format_b, not_format_c];
2106 let merged = Object::all_ofs(schemas).into_optimized();
2107
2108 assert_json_snapshot!(merged, @r#"
2109 {
2110 "allOf": [
2111 {
2112 "not": {
2113 "type": "string",
2114 "format": "date-time"
2115 }
2116 },
2117 {
2118 "not": {
2119 "type": "string",
2120 "format": "ipv4"
2121 }
2122 }
2123 ],
2124 "not": {
2125 "type": "string",
2126 "format": "email"
2127 }
2128 }
2129 "#);
2130 }
2131
2132 #[test]
2133 fn is_empty_works_parsed_from_json() {
2134 let schema: Schema = serde_json::from_str("{}").unwrap();
2135
2136 assert!(schema.is_empty());
2137 }
2138}