1use indexmap::IndexMap;
6use ordered_float::OrderedFloat;
7use serde_derive::{Deserialize, Serialize};
8
9use super::extensions::Extensions;
10use super::security::SecurityScheme;
11use super::{RefOr, Response};
12
13pub fn empty() -> Schema {
18 Schema::object(Object::builder().default(serde_json::Value::Null).build())
19}
20
21#[non_exhaustive]
29#[derive(Serialize, Deserialize, Default, Clone, PartialEq, bon::Builder)]
30#[cfg_attr(feature = "debug", derive(Debug))]
31#[serde(rename_all = "camelCase")]
32#[builder(on(_, into))]
33pub struct Components {
34 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
38 #[builder(field)]
39 pub schemas: IndexMap<String, Schema>,
40
41 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
47 #[builder(field)]
48 pub responses: IndexMap<String, RefOr<Response>>,
49
50 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
54 #[builder(field)]
55 pub security_schemes: IndexMap<String, SecurityScheme>,
56
57 #[serde(skip_serializing_if = "Option::is_none", default, flatten)]
59 pub extensions: Option<Extensions>,
60}
61
62impl Components {
63 pub fn new() -> Self {
65 Self { ..Default::default() }
66 }
67
68 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(&mut self, name: N, security_scheme: S) {
75 self.security_schemes.insert(name.into(), security_scheme.into());
76 }
77
78 pub fn add_security_schemes_from_iter<N: Into<String>, S: Into<SecurityScheme>>(
83 &mut self,
84 schemas: impl IntoIterator<Item = (N, S)>,
85 ) {
86 self.security_schemes
87 .extend(schemas.into_iter().map(|(name, item)| (name.into(), item.into())));
88 }
89
90 pub fn add_schema<N: Into<String>, S: Into<Schema>>(&mut self, name: N, scheme: S) {
95 self.schemas.insert(name.into(), scheme.into());
96 }
97
98 pub fn add_schemas_from_iter<N: Into<String>, S: Into<Schema>>(&mut self, schemas: impl IntoIterator<Item = (N, S)>) {
105 self.schemas
106 .extend(schemas.into_iter().map(|(name, item)| (name.into(), item.into())));
107 }
108}
109
110impl<S: components_builder::State> ComponentsBuilder<S> {
111 pub fn schema(mut self, name: impl Into<String>, schema: impl Into<Schema>) -> Self {
115 self.schemas.insert(name.into(), schema.into());
116 self
117 }
118
119 pub fn schemas_from_iter<I: IntoIterator<Item = (S2, C)>, C: Into<Schema>, S2: Into<String>>(
137 mut self,
138 schemas: I,
139 ) -> Self {
140 self.schemas
141 .extend(schemas.into_iter().map(|(name, schema)| (name.into(), schema.into())));
142
143 self
144 }
145
146 pub fn response<S2: Into<String>, R: Into<RefOr<Response>>>(mut self, name: S2, response: R) -> Self {
151 self.responses.insert(name.into(), response.into());
152 self
153 }
154
155 pub fn responses_from_iter<I: IntoIterator<Item = (S2, R)>, S2: Into<String>, R: Into<RefOr<Response>>>(
160 mut self,
161 responses: I,
162 ) -> Self {
163 self.responses
164 .extend(responses.into_iter().map(|(name, response)| (name.into(), response.into())));
165
166 self
167 }
168
169 pub fn security_scheme<N: Into<String>, S2: Into<SecurityScheme>>(mut self, name: N, security_scheme: S2) -> Self {
176 self.security_schemes.insert(name.into(), security_scheme.into());
177
178 self
179 }
180}
181
182impl<S: components_builder::IsComplete> From<ComponentsBuilder<S>> for Components {
183 fn from(value: ComponentsBuilder<S>) -> Self {
184 value.build()
185 }
186}
187
188impl Default for Schema {
189 fn default() -> Self {
190 Schema::Bool(true)
191 }
192}
193
194#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
199#[serde(rename_all = "camelCase")]
200#[cfg_attr(feature = "debug", derive(Debug))]
201pub struct Discriminator {
202 pub property_name: String,
205
206 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
210 pub mapping: IndexMap<String, String>,
211
212 #[serde(skip_serializing_if = "Option::is_none", flatten)]
214 pub extensions: Option<Extensions>,
215}
216
217impl Discriminator {
218 pub fn new<I: Into<String>>(property_name: I) -> Self {
228 Self {
229 property_name: property_name.into(),
230 mapping: IndexMap::new(),
231 ..Default::default()
232 }
233 }
234
235 pub fn with_mapping<P: Into<String>, M: IntoIterator<Item = (K, V)>, K: Into<String>, V: Into<String>>(
252 property_name: P,
253 mapping: M,
254 ) -> Self {
255 Self {
256 property_name: property_name.into(),
257 mapping: IndexMap::from_iter(mapping.into_iter().map(|(key, val)| (key.into(), val.into()))),
258 ..Default::default()
259 }
260 }
261}
262
263#[non_exhaustive]
268#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, bon::Builder)]
269#[cfg_attr(feature = "debug", derive(Debug))]
270#[builder(on(_, into))]
271pub struct Ref {
272 #[serde(rename = "$ref")]
274 pub ref_location: String,
275
276 #[serde(skip_serializing_if = "String::is_empty", default)]
280 #[builder(default)]
281 pub description: String,
282
283 #[serde(skip_serializing_if = "String::is_empty", default)]
286 #[builder(default)]
287 pub summary: String,
288}
289
290impl Ref {
291 pub fn new<I: Into<String>>(ref_location: I) -> Self {
294 Self {
295 ref_location: ref_location.into(),
296 ..Default::default()
297 }
298 }
299
300 pub fn from_schema_name<I: Into<String>>(schema_name: I) -> Self {
303 Self::new(format!("#/components/schemas/{}", schema_name.into()))
304 }
305
306 pub fn from_response_name<I: Into<String>>(response_name: I) -> Self {
309 Self::new(format!("#/components/responses/{}", response_name.into()))
310 }
311}
312
313impl<S: ref_builder::IsComplete> From<RefBuilder<S>> for Schema {
314 fn from(builder: RefBuilder<S>) -> Self {
315 Self::from(builder.build())
316 }
317}
318
319impl From<Ref> for Schema {
320 fn from(r: Ref) -> Self {
321 Self::object(
322 Object::builder()
323 .reference(r.ref_location)
324 .description(r.description)
325 .summary(r.summary)
326 .build(),
327 )
328 }
329}
330
331impl<T> From<T> for RefOr<T> {
332 fn from(t: T) -> Self {
333 Self::T(t)
334 }
335}
336
337#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Copy)]
340#[cfg_attr(feature = "debug", derive(Debug))]
341#[non_exhaustive]
342pub enum Type {
343 #[serde(rename = "array")]
345 Array,
346 #[serde(rename = "boolean")]
348 Boolean,
349 #[serde(rename = "integer")]
351 Integer,
352 #[serde(rename = "null")]
354 Null,
355 #[serde(rename = "number")]
357 Number,
358 #[serde(rename = "object")]
360 Object,
361 #[serde(rename = "string")]
363 String,
364}
365
366#[derive(Serialize, Deserialize, Clone, PartialEq)]
372#[cfg_attr(feature = "debug", derive(Debug))]
373#[serde(untagged)]
374pub enum Types {
375 Single(Type),
377 Multi(Vec<Type>),
379}
380
381impl From<Type> for Types {
382 fn from(value: Type) -> Self {
383 Self::Single(value)
384 }
385}
386
387impl From<Vec<Type>> for Types {
388 fn from(mut value: Vec<Type>) -> Self {
389 if value.len() == 1 {
390 Self::Single(value.remove(0))
391 } else {
392 Self::Multi(value)
393 }
394 }
395}
396
397trait IsEmpty {
398 fn is_empty(&self) -> bool;
399}
400
401impl<T> IsEmpty for Option<T> {
402 fn is_empty(&self) -> bool {
403 self.is_none()
404 }
405}
406
407impl<T> IsEmpty for Vec<T> {
408 fn is_empty(&self) -> bool {
409 Vec::is_empty(self)
410 }
411}
412
413impl<K, V> IsEmpty for IndexMap<K, V> {
414 fn is_empty(&self) -> bool {
415 IndexMap::is_empty(self)
416 }
417}
418
419impl IsEmpty for String {
420 fn is_empty(&self) -> bool {
421 self.is_empty()
422 }
423}
424
425#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Default, bon::Builder)]
428#[serde(default, deny_unknown_fields)]
429#[builder(on(_, into))]
430#[cfg_attr(feature = "debug", derive(Debug))]
431#[non_exhaustive]
432pub struct Object {
433 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
437 #[builder(field)]
438 pub properties: IndexMap<String, Schema>,
439 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
443 #[builder(field)]
444 pub examples: Vec<serde_json::Value>,
445 #[serde(rename = "prefixItems", skip_serializing_if = "IsEmpty::is_empty")]
449 #[builder(field)]
450 pub prefix_items: Option<Vec<Schema>>,
451 #[serde(rename = "enum", skip_serializing_if = "IsEmpty::is_empty")]
454 #[builder(field)]
455 pub enum_values: Option<Vec<serde_json::Value>>,
456 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
459 #[builder(field)]
460 pub required: Vec<String>,
461 #[serde(rename = "allOf", skip_serializing_if = "IsEmpty::is_empty")]
464 #[builder(field)]
465 pub all_of: Vec<Schema>,
466 #[serde(rename = "anyOf", skip_serializing_if = "IsEmpty::is_empty")]
469 #[builder(field)]
470 pub any_of: Option<Vec<Schema>>,
471 #[serde(rename = "oneOf", skip_serializing_if = "IsEmpty::is_empty")]
474 #[builder(field)]
475 pub one_of: Option<Vec<Schema>>,
476 #[serde(rename = "$id", skip_serializing_if = "IsEmpty::is_empty")]
479 #[builder(default)]
480 pub id: String,
481 #[serde(rename = "$schema", skip_serializing_if = "IsEmpty::is_empty")]
484 pub schema: Option<Schema>,
485 #[serde(rename = "$ref", skip_serializing_if = "IsEmpty::is_empty")]
488 #[builder(default, name = "reference")]
489 pub reference: String,
490 #[serde(rename = "$comment", skip_serializing_if = "IsEmpty::is_empty")]
493 #[builder(default)]
494 pub comment: String,
495 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
498 #[builder(default)]
499 pub title: String,
500 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
503 #[builder(default)]
504 pub description: String,
505 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
508 #[builder(default)]
509 pub summary: String,
510 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
513 pub default: Option<serde_json::Value>,
514 #[serde(rename = "readOnly", skip_serializing_if = "IsEmpty::is_empty")]
517 pub read_only: Option<bool>,
518 #[serde(rename = "deprecated", skip_serializing_if = "IsEmpty::is_empty")]
521 pub deprecated: Option<bool>,
522 #[serde(rename = "writeOnly", skip_serializing_if = "IsEmpty::is_empty")]
525 pub write_only: Option<bool>,
526 #[serde(rename = "multipleOf", skip_serializing_if = "IsEmpty::is_empty")]
529 pub multiple_of: Option<OrderedFloat<f64>>,
530 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
533 pub maximum: Option<OrderedFloat<f64>>,
534 #[serde(rename = "exclusiveMaximum", skip_serializing_if = "IsEmpty::is_empty")]
537 pub exclusive_maximum: Option<OrderedFloat<f64>>,
538 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
541 pub minimum: Option<OrderedFloat<f64>>,
542 #[serde(rename = "exclusiveMinimum", skip_serializing_if = "IsEmpty::is_empty")]
545 pub exclusive_minimum: Option<OrderedFloat<f64>>,
546 #[serde(rename = "maxLength", skip_serializing_if = "IsEmpty::is_empty")]
549 pub max_length: Option<u64>,
550 #[serde(rename = "minLength", skip_serializing_if = "IsEmpty::is_empty")]
553 pub min_length: Option<u64>,
554 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
557 pub pattern: Option<String>,
558 #[serde(rename = "additionalItems", skip_serializing_if = "IsEmpty::is_empty")]
561 pub additional_items: Option<Schema>,
562 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
565 pub items: Option<Schema>,
566 #[serde(rename = "maxItems", skip_serializing_if = "IsEmpty::is_empty")]
569 pub max_items: Option<u64>,
570 #[serde(rename = "minItems", skip_serializing_if = "IsEmpty::is_empty")]
573 pub min_items: Option<u64>,
574 #[serde(rename = "uniqueItems", skip_serializing_if = "IsEmpty::is_empty")]
577 pub unique_items: Option<bool>,
578 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
581 pub contains: Option<Schema>,
582 #[serde(rename = "maxProperties", skip_serializing_if = "IsEmpty::is_empty")]
585 pub max_properties: Option<u64>,
586 #[serde(rename = "minProperties", skip_serializing_if = "IsEmpty::is_empty")]
589 pub min_properties: Option<u64>,
590 #[serde(rename = "maxContains", skip_serializing_if = "IsEmpty::is_empty")]
593 pub max_contains: Option<u64>,
594 #[serde(rename = "minContains", skip_serializing_if = "IsEmpty::is_empty")]
597 pub min_contains: Option<u64>,
598 #[serde(rename = "additionalProperties", skip_serializing_if = "IsEmpty::is_empty")]
601 pub additional_properties: Option<Schema>,
602 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
605 #[builder(default)]
606 pub definitions: IndexMap<String, Schema>,
607 #[serde(rename = "patternProperties", skip_serializing_if = "IsEmpty::is_empty")]
610 #[builder(default)]
611 pub pattern_properties: IndexMap<String, Schema>,
612 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
615 #[builder(default)]
616 pub dependencies: IndexMap<String, Schema>,
617 #[serde(rename = "propertyNames", skip_serializing_if = "IsEmpty::is_empty")]
620 pub property_names: Option<Schema>,
621 #[serde(rename = "const", skip_serializing_if = "IsEmpty::is_empty")]
624 #[builder(name = "const_value")]
625 pub const_value: Option<serde_json::Value>,
626 #[serde(rename = "type", skip_serializing_if = "IsEmpty::is_empty")]
629 #[builder(name = "schema_type")]
630 pub schema_type: Option<Types>,
631 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
634 #[builder(default)]
635 pub format: String,
636 #[serde(rename = "contentMediaType", skip_serializing_if = "IsEmpty::is_empty")]
639 #[builder(default)]
640 pub content_media_type: String,
641 #[serde(rename = "contentEncoding", skip_serializing_if = "IsEmpty::is_empty")]
644 #[builder(default)]
645 pub content_encoding: String,
646 #[serde(rename = "contentSchema", skip_serializing_if = "IsEmpty::is_empty")]
649 pub content_schema: Option<Schema>,
650 #[serde(rename = "if", skip_serializing_if = "IsEmpty::is_empty")]
653 pub if_cond: Option<Schema>,
654 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
657 #[builder(name = "then_cond")]
658 pub then: Option<Schema>,
659 #[serde(rename = "else", skip_serializing_if = "IsEmpty::is_empty")]
662 pub else_cond: Option<Schema>,
663 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
666 pub not: Option<Schema>,
667 #[serde(rename = "unevaluatedItems", skip_serializing_if = "IsEmpty::is_empty")]
670 pub unevaluated_items: Option<Schema>,
671 #[serde(rename = "unevaluatedProperties", skip_serializing_if = "IsEmpty::is_empty")]
674 pub unevaluated_properties: Option<Schema>,
675 #[serde(skip_serializing_if = "IsEmpty::is_empty")]
678 pub discriminator: Option<Discriminator>,
679 #[serde(flatten)]
681 pub extensions: Option<Extensions>,
682}
683
684impl From<Ref> for Object {
685 fn from(value: Ref) -> Self {
686 Self::builder()
687 .reference(value.ref_location)
688 .description(value.description)
689 .summary(value.summary)
690 .build()
691 }
692}
693
694impl<S: object_builder::State> ObjectBuilder<S> {
695 pub fn properties<P: Into<String>, C: Into<Schema>>(mut self, properties: impl IntoIterator<Item = (P, C)>) -> Self {
697 self.properties
698 .extend(properties.into_iter().map(|(p, s)| (p.into(), s.into())));
699 self
700 }
701
702 pub fn property(mut self, name: impl Into<String>, schema: impl Into<Schema>) -> Self {
704 self.properties.insert(name.into(), schema.into());
705 self
706 }
707
708 pub fn all_of(mut self, all_of: impl Into<Schema>) -> Self {
710 self.all_of.push(all_of.into());
711 self
712 }
713
714 pub fn all_ofs<C: Into<Schema>>(mut self, all_ofs: impl IntoIterator<Item = C>) -> Self {
716 self.all_of.extend(all_ofs.into_iter().map(|s| s.into()));
717 self
718 }
719
720 pub fn any_ofs<C: Into<Schema>>(self, any_ofs: impl IntoIterator<Item = C>) -> Self {
722 any_ofs.into_iter().fold(self, |this, c| this.any_of(c))
723 }
724
725 pub fn any_of(mut self, any_of: impl Into<Schema>) -> Self {
727 self.any_of.get_or_insert_default().push(any_of.into());
728 self
729 }
730
731 pub fn one_ofs<C: Into<Schema>>(self, one_ofs: impl IntoIterator<Item = C>) -> Self {
733 one_ofs.into_iter().fold(self, |this, c| this.one_of(c))
734 }
735
736 pub fn one_of(mut self, one_of: impl Into<Schema>) -> Self {
738 self.one_of.get_or_insert_default().push(one_of.into());
739 self
740 }
741
742 pub fn enum_value(mut self, enum_value: impl Into<serde_json::Value>) -> Self {
744 self.enum_values.get_or_insert_default().push(enum_value.into());
745 self
746 }
747
748 pub fn enum_values<E: Into<serde_json::Value>>(self, enum_values: impl IntoIterator<Item = E>) -> Self {
750 enum_values.into_iter().fold(self, |this, e| this.enum_value(e))
751 }
752
753 pub fn require(mut self, require: impl Into<String>) -> Self {
755 self.required.push(require.into());
756 self
757 }
758
759 pub fn required<R: Into<String>>(self, required: impl IntoIterator<Item = R>) -> Self {
761 required.into_iter().fold(self, |this, e| this.require(e))
762 }
763
764 pub fn example(mut self, example: impl Into<serde_json::Value>) -> Self {
766 self.examples.push(example.into());
767 self
768 }
769
770 pub fn examples<E: Into<serde_json::Value>>(self, examples: impl IntoIterator<Item = E>) -> Self {
772 examples.into_iter().fold(self, |this, e| this.example(e))
773 }
774}
775
776impl<S: object_builder::IsComplete> ObjectBuilder<S> {
777 pub fn to_array(self) -> ObjectBuilder<object_builder::SetItems<object_builder::SetSchemaType>> {
779 Object::builder().schema_type(Type::Array).items(self)
780 }
781}
782
783impl<S: object_builder::IsComplete> From<ObjectBuilder<S>> for Object {
784 fn from(value: ObjectBuilder<S>) -> Self {
785 value.build()
786 }
787}
788
789impl<S: object_builder::IsComplete> From<ObjectBuilder<S>> for Schema {
790 fn from(value: ObjectBuilder<S>) -> Self {
791 value.build().into()
792 }
793}
794
795impl Object {
796 pub fn with_type(ty: impl Into<Types>) -> ObjectBuilder<object_builder::SetSchemaType> {
806 Object::builder().schema_type(ty)
807 }
808
809 pub fn int32() -> Object {
811 Object::builder()
812 .schema_type(Type::Integer)
813 .maximum(i32::MAX as f64)
814 .minimum(i32::MIN as f64)
815 .build()
816 }
817
818 pub fn int64() -> Object {
820 Object::builder()
821 .schema_type(Type::Integer)
822 .maximum(i64::MAX as f64)
823 .minimum(i64::MIN as f64)
824 .build()
825 }
826
827 pub fn uint32() -> Object {
829 Object::builder()
830 .schema_type(Type::Integer)
831 .maximum(u32::MAX as f64)
832 .minimum(u32::MIN as f64)
833 .build()
834 }
835
836 pub fn uint64() -> Object {
838 Object::builder()
839 .schema_type(Type::Integer)
840 .maximum(u64::MAX as f64)
841 .minimum(u64::MIN as f64)
842 .build()
843 }
844
845 pub fn to_array(self) -> Self {
847 Self::builder().schema_type(Type::Array).items(self).build()
848 }
849
850 pub fn all_ofs<S: Into<Schema>>(all_ofs: impl IntoIterator<Item = S>) -> Object {
860 Object::builder().all_ofs(all_ofs).build()
861 }
862}
863
864macro_rules! iter_chain {
865 ($($item:expr),*$(,)?) => {
866 std::iter::empty()
867 $(.chain($item))*
868 };
869}
870
871macro_rules! merge_item {
872 ([$self:ident, $other:ident] => { $($item:ident => $merge_behaviour:expr),*$(,)? }) => {$({
873 let self_item = &mut $self.$item;
874 let other_item = &mut $other.$item;
875 if IsEmpty::is_empty(self_item) {
876 *self_item = std::mem::take(other_item);
877 } else if self_item == other_item {
878 std::mem::take(other_item);
879 } else if !IsEmpty::is_empty(other_item) {
880 $merge_behaviour(self_item, other_item);
881 }
882 })*};
883}
884
885fn dedupe_array<T: PartialEq>(items: &mut Vec<T>) {
886 let mut dedupe = Vec::new();
887 for item in items.drain(..) {
888 if !dedupe.contains(&item) {
889 dedupe.push(item);
890 }
891 }
892
893 *items = dedupe;
894}
895
896impl Object {
897 pub fn optimize(&mut self) {
900 let mut all_ofs = Vec::new();
902 self.take_all_ofs(&mut all_ofs);
903
904 all_ofs
905 .iter_mut()
906 .filter_map(|schema| schema.as_object_mut())
907 .for_each(|schema| self.merge(schema));
908
909 let sub_schemas = iter_chain!(
911 self.schema.iter_mut(),
912 self.additional_items.iter_mut(),
913 self.contains.iter_mut(),
914 self.additional_properties.iter_mut(),
915 self.items.iter_mut(),
916 self.prefix_items.iter_mut().flatten(),
917 self.definitions.values_mut(),
918 self.properties.values_mut(),
919 self.pattern_properties.values_mut(),
920 self.dependencies.values_mut(),
921 self.property_names.iter_mut(),
922 self.if_cond.iter_mut(),
923 self.then.iter_mut(),
924 self.else_cond.iter_mut(),
925 self.any_of.iter_mut().flatten(),
926 self.one_of.iter_mut().flatten(),
927 self.not.iter_mut(),
928 self.unevaluated_items.iter_mut(),
929 self.unevaluated_properties.iter_mut(),
930 self.content_schema.iter_mut(),
931 );
932
933 for schema in sub_schemas {
934 schema.optimize();
935 }
936
937 self.all_of = all_ofs.into_iter().filter(|schema| !schema.is_empty()).collect();
938 dedupe_array(&mut self.examples);
939 dedupe_array(&mut self.required);
940 if let Some(_enum) = &mut self.enum_values {
941 dedupe_array(_enum);
942 }
943 dedupe_array(&mut self.all_of);
944 if let Some(any_of) = &mut self.any_of {
945 dedupe_array(any_of);
946 }
947 if let Some(one_of) = &mut self.one_of {
948 dedupe_array(one_of);
949 }
950 }
951
952 pub fn into_optimized(mut self) -> Self {
954 self.optimize();
955 self
956 }
957
958 pub fn is_empty(&self) -> bool {
960 static DEFAULT: std::sync::LazyLock<Object> = std::sync::LazyLock::new(Object::default);
961
962 self == &*DEFAULT
963 }
964
965 fn take_all_ofs(&mut self, collection: &mut Vec<Schema>) {
966 for mut schema in self.all_of.drain(..) {
967 schema.take_all_ofs(collection);
968 collection.push(schema);
969 }
970 }
971
972 fn merge(&mut self, other: &mut Self) {
973 merge_item!(
974 [self, other] => {
975 id => merge_skip,
976 schema => merge_sub_schema,
977 reference => merge_skip,
978 comment => merge_drop_second,
979 title => merge_drop_second,
980 description => merge_drop_second,
981 summary => merge_drop_second,
982 default => merge_drop_second,
983 read_only => merge_set_true,
984 examples => merge_array_combine,
985 multiple_of => merge_multiple_of,
986 maximum => merge_min,
987 exclusive_maximum => merge_min,
988 minimum => merge_max,
989 exclusive_minimum => merge_min,
990 max_length => merge_min,
991 min_length => merge_max,
992 pattern => merge_skip,
993 additional_items => merge_sub_schema,
994 items => merge_sub_schema,
995 prefix_items => merge_prefix_items,
996 max_items => merge_min,
997 min_items => merge_max,
998 unique_items => merge_set_true,
999 contains => merge_sub_schema,
1000 max_properties => merge_min,
1001 min_properties => merge_max,
1002 max_contains => merge_min,
1003 min_contains => merge_max,
1004 required => merge_array_combine,
1005 additional_properties => merge_sub_schema,
1006 definitions => merge_schema_map,
1007 properties => merge_schema_map,
1008 pattern_properties => merge_schema_map,
1009 dependencies => merge_schema_map,
1010 property_names => merge_sub_schema,
1011 const_value => merge_skip,
1012 enum_values => merge_array_union_optional,
1013 schema_type => merge_type,
1014 format => merge_skip,
1015 content_media_type => merge_skip,
1016 content_encoding => merge_skip,
1017 any_of => merge_array_combine_optional,
1021 one_of => merge_array_combine_optional,
1022 not => merge_sub_schema,
1023 unevaluated_items => merge_sub_schema,
1024 unevaluated_properties => merge_sub_schema,
1025 deprecated => merge_set_true,
1026 write_only => merge_set_true,
1027 content_schema => merge_sub_schema,
1028 }
1029 );
1030 }
1031}
1032
1033fn merge_skip<T>(_: &mut T, _: &mut T) {}
1034
1035fn merge_drop_second<T: Default>(_: &mut T, other: &mut T) {
1036 std::mem::take(other);
1037}
1038
1039fn merge_min<T: Ord + Copy>(value: &mut Option<T>, other: &mut Option<T>) {
1040 let value = value.as_mut().unwrap();
1041 let other = other.take().unwrap();
1042 *value = (*value).min(other);
1043}
1044
1045fn merge_max<T: Ord + Copy>(value: &mut Option<T>, other: &mut Option<T>) {
1046 let value = value.as_mut().unwrap();
1047 let other = other.take().unwrap();
1048 *value = (*value).max(other);
1049}
1050
1051fn merge_set_true(value: &mut Option<bool>, other: &mut Option<bool>) {
1052 other.take();
1053 value.replace(true);
1054}
1055
1056fn merge_sub_schema(value: &mut Option<Schema>, other_opt: &mut Option<Schema>) {
1057 let value = value.as_mut().unwrap();
1058 let mut other = other_opt.take().unwrap();
1059 value.merge(&mut other);
1060 if !other.is_empty() {
1061 other_opt.replace(other);
1062 }
1063}
1064
1065fn merge_array_combine<T: PartialEq>(value: &mut Vec<T>, other: &mut Vec<T>) {
1066 value.append(other);
1067}
1068
1069fn merge_array_union<T: PartialEq>(value: &mut Vec<T>, other: &mut Vec<T>) {
1070 let other = std::mem::take(other);
1071 value.retain(|v| other.contains(v));
1072}
1073
1074fn merge_array_union_optional<T: PartialEq>(value: &mut Option<Vec<T>>, other: &mut Option<Vec<T>>) {
1075 merge_array_union(value.as_mut().unwrap(), other.as_mut().unwrap());
1076 if other.as_ref().is_some_and(|o| o.is_empty()) {
1077 other.take();
1078 }
1079}
1080
1081fn merge_array_combine_optional<T: PartialEq>(value: &mut Option<Vec<T>>, other: &mut Option<Vec<T>>) {
1082 merge_array_combine(value.as_mut().unwrap(), other.as_mut().unwrap());
1083 if other.as_ref().is_some_and(|o| o.is_empty()) {
1084 other.take();
1085 }
1086}
1087
1088fn merge_schema_map(value: &mut IndexMap<String, Schema>, other: &mut IndexMap<String, Schema>) {
1089 for (key, mut other) in other.drain(..) {
1090 match value.entry(key) {
1091 indexmap::map::Entry::Occupied(mut value) => {
1092 value.get_mut().merge(&mut other);
1093 if !other.is_empty() {
1094 if let Some(obj) = value.get_mut().as_object_mut() {
1095 obj.all_of.push(other);
1096 }
1097 }
1098 }
1099 indexmap::map::Entry::Vacant(v) => {
1100 v.insert(other);
1101 }
1102 }
1103 }
1104}
1105
1106fn merge_type(value: &mut Option<Types>, other: &mut Option<Types>) {
1107 match (value.as_mut().unwrap(), other.take().unwrap()) {
1108 (Types::Single(s), Types::Single(ref o)) if s != o => {
1109 value.replace(Types::Multi(Vec::new()));
1110 }
1111 (Types::Single(_), Types::Single(_)) => {}
1112 (Types::Multi(s), Types::Multi(ref mut o)) => {
1113 merge_array_union(s, o);
1114 }
1115 (&mut Types::Single(s), Types::Multi(ref o)) | (&mut Types::Multi(ref o), Types::Single(s)) => {
1116 if o.contains(&s) {
1117 value.replace(Types::Single(s));
1118 } else {
1119 value.replace(Types::Multi(Vec::new()));
1120 }
1121 }
1122 }
1123}
1124
1125fn merge_prefix_items(value: &mut Option<Vec<Schema>>, other: &mut Option<Vec<Schema>>) {
1126 let mut other = other.take().unwrap_or_default();
1127 let value = value.as_mut().unwrap();
1128 value.extend(other.drain(value.len()..));
1129 for (value, mut other) in value.iter_mut().zip(other) {
1130 value.merge(&mut other);
1131 if !other.is_empty() {
1132 if let Some(obj) = value.as_object_mut() {
1133 obj.all_of.push(other);
1134 }
1135 }
1136 }
1137}
1138
1139fn merge_multiple_of(value: &mut Option<OrderedFloat<f64>>, other: &mut Option<OrderedFloat<f64>>) {
1140 let value = value.as_mut().unwrap().as_mut();
1141 let other = other.take().unwrap().into_inner();
1142
1143 fn gcd_f64(mut a: f64, mut b: f64) -> f64 {
1144 a = a.abs();
1145 b = b.abs();
1146 if a == 0.0 {
1148 return b;
1149 }
1150 if b == 0.0 {
1151 return a;
1152 }
1153 while b > 0.0 {
1155 let r = a % b;
1156 a = b;
1157 b = r;
1158 }
1159 a
1160 }
1161
1162 fn lcm_f64(a: f64, b: f64) -> f64 {
1164 if a == 0.0 || b == 0.0 {
1165 return 0.0;
1166 }
1167 let g = gcd_f64(a, b);
1168 (a / g * b).abs()
1170 }
1171
1172 *value = lcm_f64(*value, other);
1173}
1174
1175#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq)]
1177#[cfg_attr(feature = "debug", derive(Debug))]
1178#[serde(untagged)]
1179#[non_exhaustive]
1180pub enum Schema {
1181 Object(Box<Object>),
1183 Bool(bool),
1185}
1186
1187impl From<Object> for Schema {
1188 fn from(value: Object) -> Self {
1189 Self::object(value)
1190 }
1191}
1192
1193impl From<bool> for Schema {
1194 fn from(value: bool) -> Self {
1195 Self::Bool(value)
1196 }
1197}
1198
1199impl Schema {
1200 pub fn to_array(self) -> Self {
1202 Self::object(Object::builder().schema_type(Type::Array).items(self))
1203 }
1204
1205 pub fn optimize(&mut self) {
1207 match self {
1208 Self::Bool(_) => {}
1209 Self::Object(obj) => obj.optimize(),
1210 }
1211 }
1212
1213 pub fn into_optimized(mut self) -> Self {
1215 match &mut self {
1216 Self::Bool(_) => {}
1217 Self::Object(obj) => obj.optimize(),
1218 }
1219 self
1220 }
1221
1222 pub fn object(value: impl Into<Object>) -> Self {
1224 Self::Object(value.into().into())
1225 }
1226
1227 fn take_all_ofs(&mut self, collection: &mut Vec<Schema>) {
1228 match self {
1229 Self::Bool(_) => {}
1230 Self::Object(obj) => obj.take_all_ofs(collection),
1231 }
1232 }
1233
1234 pub fn is_empty(&self) -> bool {
1236 match self {
1237 Self::Bool(result) => *result,
1238 Self::Object(obj) => obj.is_empty(),
1239 }
1240 }
1241
1242 fn as_object_mut(&mut self) -> Option<&mut Object> {
1243 match self {
1244 Self::Bool(_) => None,
1245 Self::Object(obj) => Some(obj.as_mut()),
1246 }
1247 }
1248
1249 fn merge(&mut self, other: &mut Self) {
1250 match (self, other) {
1251 (this @ Schema::Bool(false), _) | (this, Schema::Bool(false)) => {
1252 *this = Schema::Bool(false);
1253 }
1254 (this @ Schema::Bool(true), other) => {
1255 std::mem::swap(this, other);
1256 }
1257 (_, Schema::Bool(true)) => {}
1258 (Schema::Object(value), Schema::Object(other)) => {
1259 value.merge(other.as_mut());
1260 }
1261 }
1262 }
1263}
1264
1265#[cfg(test)]
1266#[cfg_attr(coverage_nightly, coverage(off))]
1267mod tests {
1268 use insta::assert_json_snapshot;
1269 use serde_json::{Value, json};
1270
1271 use super::*;
1272 use crate::*;
1273
1274 #[test]
1275 fn create_schema_serializes_json() -> Result<(), serde_json::Error> {
1276 let openapi = OpenApi::builder()
1277 .info(Info::new("My api", "1.0.0"))
1278 .paths(Paths::new())
1279 .components(
1280 Components::builder()
1281 .schema("Person", Ref::new("#/components/PersonModel"))
1282 .schema(
1283 "Credential",
1284 Schema::from(
1285 Object::builder()
1286 .property(
1287 "id",
1288 Object::builder()
1289 .schema_type(Type::Integer)
1290 .format("int32")
1291 .description("Id of credential")
1292 .default(1i32),
1293 )
1294 .property(
1295 "name",
1296 Object::builder().schema_type(Type::String).description("Name of credential"),
1297 )
1298 .property(
1299 "status",
1300 Object::builder()
1301 .schema_type(Type::String)
1302 .default("Active")
1303 .description("Credential status")
1304 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1305 )
1306 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1307 .property("tags", Object::builder().schema_type(Type::String).build().to_array()),
1308 ),
1309 )
1310 .build(),
1311 )
1312 .build();
1313
1314 let serialized = serde_json::to_string_pretty(&openapi)?;
1315 println!("serialized json:\n {serialized}");
1316
1317 let value = serde_json::to_value(&openapi)?;
1318 let credential = get_json_path(&value, "components.schemas.Credential.properties");
1319 let person = get_json_path(&value, "components.schemas.Person");
1320
1321 assert!(
1322 credential.get("id").is_some(),
1323 "could not find path: components.schemas.Credential.properties.id"
1324 );
1325 assert!(
1326 credential.get("status").is_some(),
1327 "could not find path: components.schemas.Credential.properties.status"
1328 );
1329 assert!(
1330 credential.get("name").is_some(),
1331 "could not find path: components.schemas.Credential.properties.name"
1332 );
1333 assert!(
1334 credential.get("history").is_some(),
1335 "could not find path: components.schemas.Credential.properties.history"
1336 );
1337 assert_eq!(
1338 credential.get("id").unwrap_or(&serde_json::value::Value::Null).to_string(),
1339 r#"{"default":1,"description":"Id of credential","format":"int32","type":"integer"}"#,
1340 "components.schemas.Credential.properties.id did not match"
1341 );
1342 assert_eq!(
1343 credential.get("name").unwrap_or(&serde_json::value::Value::Null).to_string(),
1344 r#"{"description":"Name of credential","type":"string"}"#,
1345 "components.schemas.Credential.properties.name did not match"
1346 );
1347 assert_eq!(
1348 credential
1349 .get("status")
1350 .unwrap_or(&serde_json::value::Value::Null)
1351 .to_string(),
1352 r#"{"default":"Active","description":"Credential status","enum":["Active","NotActive","Locked","Expired"],"type":"string"}"#,
1353 "components.schemas.Credential.properties.status did not match"
1354 );
1355 assert_eq!(
1356 credential
1357 .get("history")
1358 .unwrap_or(&serde_json::value::Value::Null)
1359 .to_string(),
1360 r###"{"items":{"$ref":"#/components/schemas/UpdateHistory"},"type":"array"}"###,
1361 "components.schemas.Credential.properties.history did not match"
1362 );
1363 assert_eq!(
1364 person.to_string(),
1365 r###"{"$ref":"#/components/PersonModel"}"###,
1366 "components.schemas.Person.ref did not match"
1367 );
1368
1369 Ok(())
1370 }
1371
1372 #[test]
1374 fn test_property_order() {
1375 let json_value = Object::builder()
1376 .property(
1377 "id",
1378 Object::builder()
1379 .schema_type(Type::Integer)
1380 .format("int32")
1381 .description("Id of credential")
1382 .default(1i32),
1383 )
1384 .property(
1385 "name",
1386 Object::builder().schema_type(Type::String).description("Name of credential"),
1387 )
1388 .property(
1389 "status",
1390 Object::builder()
1391 .schema_type(Type::String)
1392 .default("Active")
1393 .description("Credential status")
1394 .enum_values(["Active", "NotActive", "Locked", "Expired"]),
1395 )
1396 .property("history", Schema::from(Ref::from_schema_name("UpdateHistory")).to_array())
1397 .property("tags", Object::builder().schema_type(Type::String).to_array())
1398 .build();
1399
1400 assert_eq!(
1401 json_value.properties.keys().collect::<Vec<_>>(),
1402 vec!["id", "name", "status", "history", "tags"]
1403 );
1404 }
1405
1406 #[test]
1408 fn test_additional_properties() {
1409 let json_value = Object::builder()
1410 .schema_type(Type::Object)
1411 .additional_properties(Object::builder().schema_type(Type::String))
1412 .build();
1413 assert_json_snapshot!(json_value, @r#"
1414 {
1415 "additionalProperties": {
1416 "type": "string"
1417 },
1418 "type": "object"
1419 }
1420 "#);
1421
1422 let json_value = Object::builder()
1423 .schema_type(Type::Object)
1424 .additional_properties(Object::builder().schema_type(Type::Number).to_array())
1425 .build();
1426
1427 assert_json_snapshot!(json_value, @r#"
1428 {
1429 "additionalProperties": {
1430 "items": {
1431 "type": "number"
1432 },
1433 "type": "array"
1434 },
1435 "type": "object"
1436 }
1437 "#);
1438
1439 let json_value = Object::builder()
1440 .schema_type(Type::Object)
1441 .additional_properties(Ref::from_schema_name("ComplexModel"))
1442 .build();
1443 assert_json_snapshot!(json_value, @r##"
1444 {
1445 "additionalProperties": {
1446 "$ref": "#/components/schemas/ComplexModel"
1447 },
1448 "type": "object"
1449 }
1450 "##);
1451 }
1452
1453 #[test]
1454 fn test_object_with_title() {
1455 let json_value = Object::builder().schema_type(Type::Object).title("SomeName").build();
1456 assert_json_snapshot!(json_value, @r#"
1457 {
1458 "title": "SomeName",
1459 "type": "object"
1460 }
1461 "#);
1462 }
1463
1464 #[test]
1465 fn derive_object_with_examples() {
1466 let json_value = Object::builder()
1467 .schema_type(Type::Object)
1468 .examples([json!({"age": 20, "name": "bob the cat"})])
1469 .build();
1470 assert_json_snapshot!(json_value, @r#"
1471 {
1472 "examples": [
1473 {
1474 "age": 20,
1475 "name": "bob the cat"
1476 }
1477 ],
1478 "type": "object"
1479 }
1480 "#);
1481 }
1482
1483 fn get_json_path<'a>(value: &'a Value, path: &str) -> &'a Value {
1484 path.split('.').fold(value, |acc, fragment| {
1485 acc.get(fragment).unwrap_or(&serde_json::value::Value::Null)
1486 })
1487 }
1488
1489 #[test]
1490 fn test_array_new() {
1491 let array = Object::builder()
1492 .property(
1493 "id",
1494 Object::builder()
1495 .schema_type(Type::Integer)
1496 .format("int32")
1497 .description("Id of credential")
1498 .default(json!(1i32)),
1499 )
1500 .to_array()
1501 .build();
1502
1503 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1504 }
1505
1506 #[test]
1507 fn test_array_builder() {
1508 let array = Object::builder()
1509 .schema_type(Type::Array)
1510 .items(
1511 Object::builder().property(
1512 "id",
1513 Object::builder()
1514 .schema_type(Type::Integer)
1515 .format("int32")
1516 .description("Id of credential")
1517 .default(1i32),
1518 ),
1519 )
1520 .build();
1521
1522 assert!(matches!(array.schema_type, Some(Types::Single(Type::Array))));
1523 }
1524
1525 #[test]
1526 fn reserialize_deserialized_schema_components() {
1527 let components = Components::builder()
1528 .schemas_from_iter([(
1529 "Comp",
1530 Schema::from(
1531 Object::builder()
1532 .property("name", Object::builder().schema_type(Type::String))
1533 .required(["name"]),
1534 ),
1535 )])
1536 .responses_from_iter(vec![("200", Response::builder().description("Okay").build())])
1537 .security_scheme(
1538 "TLS",
1539 SecurityScheme::MutualTls {
1540 description: None,
1541 extensions: None,
1542 },
1543 )
1544 .build();
1545
1546 let serialized_components = serde_json::to_string(&components).unwrap();
1547
1548 let deserialized_components: Components = serde_json::from_str(serialized_components.as_str()).unwrap();
1549
1550 assert_eq!(
1551 serialized_components,
1552 serde_json::to_string(&deserialized_components).unwrap()
1553 )
1554 }
1555
1556 #[test]
1557 fn reserialize_deserialized_object_component() {
1558 let prop = Object::builder()
1559 .property("name", Object::builder().schema_type(Type::String))
1560 .required(["name"])
1561 .build();
1562
1563 let serialized_components = serde_json::to_string(&prop).unwrap();
1564 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1565
1566 assert_eq!(
1567 serialized_components,
1568 serde_json::to_string(&deserialized_components).unwrap()
1569 )
1570 }
1571
1572 #[test]
1573 fn reserialize_deserialized_property() {
1574 let prop = Object::builder().schema_type(Type::String).build();
1575
1576 let serialized_components = serde_json::to_string(&prop).unwrap();
1577 let deserialized_components: Object = serde_json::from_str(serialized_components.as_str()).unwrap();
1578
1579 assert_eq!(
1580 serialized_components,
1581 serde_json::to_string(&deserialized_components).unwrap()
1582 )
1583 }
1584
1585 #[test]
1586 fn deserialize_reserialize_one_of_default_type() {
1587 let a = Object::builder()
1588 .one_ofs([
1589 Object::builder().property("element", Ref::new("#/test")),
1590 Object::builder().property("foobar", Ref::new("#/foobar")),
1591 ])
1592 .build();
1593
1594 let serialized_json = serde_json::to_string(&a).expect("should serialize to json");
1595 let b: Object = serde_json::from_str(&serialized_json).expect("should deserialize OneOf");
1596 let reserialized_json = serde_json::to_string(&b).expect("reserialized json");
1597
1598 println!("{serialized_json}");
1599 println!("{reserialized_json}",);
1600 assert_eq!(serialized_json, reserialized_json);
1601 }
1602
1603 #[test]
1604 fn serialize_deserialize_any_of_of_within_ref_or_t_object_builder() {
1605 let ref_or_schema = Object::builder()
1606 .property(
1607 "test",
1608 Object::builder()
1609 .any_ofs([
1610 Object::builder().property("element", Ref::new("#/test")).build().to_array(),
1611 Object::builder().property("foobar", Ref::new("#/foobar")).build(),
1612 ])
1613 .build(),
1614 )
1615 .build();
1616
1617 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1618 println!("----------------------------");
1619 println!("{json_str}");
1620
1621 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1622
1623 let json_de_str = serde_json::to_string(&deserialized).expect("");
1624 println!("----------------------------");
1625 println!("{json_de_str}");
1626 assert!(json_str.contains("\"anyOf\""));
1627 assert_eq!(json_str, json_de_str);
1628 }
1629
1630 #[test]
1631 fn serialize_deserialize_schema_array_ref_or_t() {
1632 let ref_or_schema = Object::builder()
1633 .property("element", Ref::new("#/test"))
1634 .to_array()
1635 .to_array()
1636 .build();
1637
1638 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1639 println!("----------------------------");
1640 println!("{json_str}");
1641
1642 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1643
1644 let json_de_str = serde_json::to_string(&deserialized).expect("");
1645 println!("----------------------------");
1646 println!("{json_de_str}");
1647
1648 assert_eq!(json_str, json_de_str);
1649 }
1650
1651 #[test]
1652 fn serialize_deserialize_schema_array_builder() {
1653 let ref_or_schema = Object::builder().property("element", Ref::new("#/test")).build().to_array();
1654
1655 let json_str = serde_json::to_string(&ref_or_schema).expect("");
1656 println!("----------------------------");
1657 println!("{json_str}");
1658
1659 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
1660
1661 let json_de_str = serde_json::to_string(&deserialized).expect("");
1662 println!("----------------------------");
1663 println!("{json_de_str}");
1664
1665 assert_eq!(json_str, json_de_str);
1666 }
1667
1668 #[test]
1669 fn serialize_deserialize_schema_with_additional_properties() {
1670 let schema = Object::builder()
1671 .property("map", Object::builder().additional_properties(true))
1672 .build();
1673
1674 let json_str = serde_json::to_string(&schema).unwrap();
1675 println!("----------------------------");
1676 println!("{json_str}");
1677
1678 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1679
1680 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1681 println!("----------------------------");
1682 println!("{json_de_str}");
1683
1684 assert_eq!(json_str, json_de_str);
1685 }
1686
1687 #[test]
1688 fn serialize_deserialize_schema_with_additional_properties_object() {
1689 let schema = Object::builder()
1690 .property(
1691 "map",
1692 Object::builder()
1693 .additional_properties(Object::builder().property("name", Object::builder().schema_type(Type::String))),
1694 )
1695 .build();
1696
1697 let json_str = serde_json::to_string(&schema).unwrap();
1698 println!("----------------------------");
1699 println!("{json_str}");
1700
1701 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
1702
1703 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1704 println!("----------------------------");
1705 println!("{json_de_str}");
1706
1707 assert_eq!(json_str, json_de_str);
1708 }
1709
1710 #[test]
1711 fn serialize_discriminator_with_mapping() {
1712 let mut discriminator = Discriminator::new("type");
1713 discriminator.mapping = [("int".to_string(), "#/components/schemas/MyInt".to_string())]
1714 .into_iter()
1715 .collect::<IndexMap<_, _>>();
1716 let one_of = Object::builder()
1717 .one_of(Ref::from_schema_name("MyInt"))
1718 .discriminator(discriminator)
1719 .build();
1720 assert_json_snapshot!(one_of, @r##"
1721 {
1722 "oneOf": [
1723 {
1724 "$ref": "#/components/schemas/MyInt"
1725 }
1726 ],
1727 "discriminator": {
1728 "propertyName": "type",
1729 "mapping": {
1730 "int": "#/components/schemas/MyInt"
1731 }
1732 }
1733 }
1734 "##);
1735 }
1736
1737 #[test]
1738 fn serialize_deserialize_object_with_multiple_schema_types() {
1739 let object = Object::builder().schema_type(vec![Type::Object, Type::Null]).build();
1740
1741 let json_str = serde_json::to_string(&object).unwrap();
1742 println!("----------------------------");
1743 println!("{json_str}");
1744
1745 let deserialized: Object = serde_json::from_str(&json_str).unwrap();
1746
1747 let json_de_str = serde_json::to_string(&deserialized).unwrap();
1748 println!("----------------------------");
1749 println!("{json_de_str}");
1750
1751 assert_eq!(json_str, json_de_str);
1752 }
1753
1754 #[test]
1755 fn object_with_extensions() {
1756 let expected = json!("value");
1757 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1758 let json_value = Object::builder().extensions(extensions).build();
1759
1760 let value = serde_json::to_value(&json_value).unwrap();
1761 assert_eq!(value.get("x-some-extension"), Some(&expected));
1762 }
1763
1764 #[test]
1765 fn array_with_extensions() {
1766 let expected = json!("value");
1767 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1768 let json_value = Object::builder().extensions(extensions).to_array().build();
1769
1770 let value = serde_json::to_value(&json_value).unwrap();
1771 assert_eq!(value["items"].get("x-some-extension"), Some(&expected));
1772 }
1773
1774 #[test]
1775 fn oneof_with_extensions() {
1776 let expected = json!("value");
1777 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1778 let json_value = Object::builder()
1779 .one_of(Object::builder().extensions(extensions).build())
1780 .build();
1781
1782 let value = serde_json::to_value(&json_value).unwrap();
1783 assert_eq!(value["oneOf"][0].get("x-some-extension"), Some(&expected));
1784 }
1785
1786 #[test]
1787 fn allof_with_extensions() {
1788 let expected = json!("value");
1789 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1790 let json_value = Object::builder()
1791 .all_of(Object::builder().extensions(extensions).build())
1792 .build();
1793
1794 let value = serde_json::to_value(&json_value).unwrap();
1795 assert_eq!(value["allOf"][0].get("x-some-extension"), Some(&expected));
1796 }
1797
1798 #[test]
1799 fn anyof_with_extensions() {
1800 let expected = json!("value");
1801 let extensions = extensions::Extensions::default().add("x-some-extension", expected.clone());
1802 let json_value = Object::builder()
1803 .any_of(Object::builder().extensions(extensions).build())
1804 .build();
1805
1806 let value = serde_json::to_value(&json_value).unwrap();
1807 assert_eq!(value["anyOf"][0].get("x-some-extension"), Some(&expected));
1808 }
1809}