1use std::collections::BTreeMap;
6
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use super::extensions::Extensions;
11use super::RefOr;
12use super::{builder, security::SecurityScheme, set_value, xml::Xml, Deprecated, Response};
13use crate::{ToResponse, ToSchema};
14
15macro_rules! component_from_builder {
16 ( $name:ident ) => {
17 impl From<$name> for Schema {
18 fn from(builder: $name) -> Self {
19 builder.build().into()
20 }
21 }
22 };
23}
24
25macro_rules! to_array_builder {
26 () => {
27 pub fn to_array_builder(self) -> ArrayBuilder {
29 ArrayBuilder::from(Array::new(self))
30 }
31 };
32}
33
34pub fn empty() -> Schema {
39 Schema::Object(
40 ObjectBuilder::new()
41 .schema_type(SchemaType::AnyValue)
42 .default(Some(serde_json::Value::Null))
43 .into(),
44 )
45}
46
47builder! {
48 ComponentsBuilder;
49
50 #[non_exhaustive]
58 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
59 #[cfg_attr(feature = "debug", derive(Debug))]
60 #[serde(rename_all = "camelCase")]
61 pub struct Components {
62 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
66 pub schemas: BTreeMap<String, RefOr<Schema>>,
67
68 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
74 pub responses: BTreeMap<String, RefOr<Response>>,
75
76 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
80 pub security_schemes: BTreeMap<String, SecurityScheme>,
81
82 #[serde(skip_serializing_if = "Option::is_none", flatten)]
84 pub extensions: Option<Extensions>,
85 }
86}
87
88impl Components {
89 pub fn new() -> Self {
91 Self {
92 ..Default::default()
93 }
94 }
95 pub fn add_security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
102 &mut self,
103 name: N,
104 security_scheme: S,
105 ) {
106 self.security_schemes
107 .insert(name.into(), security_scheme.into());
108 }
109
110 pub fn add_security_schemes_from_iter<
117 I: IntoIterator<Item = (N, S)>,
118 N: Into<String>,
119 S: Into<SecurityScheme>,
120 >(
121 &mut self,
122 schemas: I,
123 ) {
124 self.security_schemes.extend(
125 schemas
126 .into_iter()
127 .map(|(name, item)| (name.into(), item.into())),
128 );
129 }
130}
131
132impl ComponentsBuilder {
133 pub fn schema<S: Into<String>, I: Into<RefOr<Schema>>>(mut self, name: S, schema: I) -> Self {
137 self.schemas.insert(name.into(), schema.into());
138
139 self
140 }
141
142 pub fn schema_from<I: ToSchema>(mut self) -> Self {
159 let name = I::name();
160 let schema = I::schema();
161 self.schemas.insert(name.to_string(), schema);
162
163 self
164 }
165
166 pub fn schemas_from_iter<
185 I: IntoIterator<Item = (S, C)>,
186 C: Into<RefOr<Schema>>,
187 S: Into<String>,
188 >(
189 mut self,
190 schemas: I,
191 ) -> Self {
192 self.schemas.extend(
193 schemas
194 .into_iter()
195 .map(|(name, schema)| (name.into(), schema.into())),
196 );
197
198 self
199 }
200
201 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
206 mut self,
207 name: S,
208 response: R,
209 ) -> Self {
210 self.responses.insert(name.into(), response.into());
211 self
212 }
213
214 pub fn response_from<'r, I: ToResponse<'r>>(self) -> Self {
220 let (name, response) = I::response();
221 self.response(name, response)
222 }
223
224 pub fn responses_from_iter<
229 I: IntoIterator<Item = (S, R)>,
230 S: Into<String>,
231 R: Into<RefOr<Response>>,
232 >(
233 mut self,
234 responses: I,
235 ) -> Self {
236 self.responses.extend(
237 responses
238 .into_iter()
239 .map(|(name, response)| (name.into(), response.into())),
240 );
241
242 self
243 }
244
245 pub fn security_scheme<N: Into<String>, S: Into<SecurityScheme>>(
252 mut self,
253 name: N,
254 security_scheme: S,
255 ) -> Self {
256 self.security_schemes
257 .insert(name.into(), security_scheme.into());
258
259 self
260 }
261
262 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
264 set_value!(self extensions extensions)
265 }
266}
267
268#[non_exhaustive]
273#[derive(Serialize, Deserialize, Clone, PartialEq)]
274#[cfg_attr(feature = "debug", derive(Debug))]
275#[serde(untagged, rename_all = "camelCase")]
276pub enum Schema {
277 Array(Array),
280 Object(Object),
283 OneOf(OneOf),
289
290 AllOf(AllOf),
294
295 AnyOf(AnyOf),
299}
300
301impl Default for Schema {
302 fn default() -> Self {
303 Schema::Object(Object::default())
304 }
305}
306
307#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
312#[serde(rename_all = "camelCase")]
313#[cfg_attr(feature = "debug", derive(Debug))]
314pub struct Discriminator {
315 pub property_name: String,
318
319 #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
323 pub mapping: BTreeMap<String, String>,
324
325 #[serde(skip_serializing_if = "Option::is_none", flatten)]
327 pub extensions: Option<Extensions>,
328}
329
330impl Discriminator {
331 pub fn new<I: Into<String>>(property_name: I) -> Self {
341 Self {
342 property_name: property_name.into(),
343 mapping: BTreeMap::new(),
344 ..Default::default()
345 }
346 }
347
348 pub fn with_mapping<
365 P: Into<String>,
366 M: IntoIterator<Item = (K, V)>,
367 K: Into<String>,
368 V: Into<String>,
369 >(
370 property_name: P,
371 mapping: M,
372 ) -> Self {
373 Self {
374 property_name: property_name.into(),
375 mapping: BTreeMap::from_iter(
376 mapping
377 .into_iter()
378 .map(|(key, val)| (key.into(), val.into())),
379 ),
380 ..Default::default()
381 }
382 }
383}
384
385builder! {
386 OneOfBuilder;
387
388 #[derive(Serialize, Deserialize, Clone, PartialEq)]
395 #[cfg_attr(feature = "debug", derive(Debug))]
396 pub struct OneOf {
397 #[serde(rename = "oneOf")]
399 pub items: Vec<RefOr<Schema>>,
400
401 #[serde(rename = "type", default = "SchemaType::any", skip_serializing_if = "SchemaType::is_any_value")]
406 pub schema_type: SchemaType,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
410 pub title: Option<String>,
411
412 #[serde(skip_serializing_if = "Option::is_none")]
414 pub description: Option<String>,
415
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub default: Option<Value>,
419
420 #[serde(skip_serializing_if = "Option::is_none")]
424 pub example: Option<Value>,
425
426 #[serde(skip_serializing_if = "Vec::is_empty", default)]
428 pub examples: Vec<Value>,
429
430 #[serde(skip_serializing_if = "Option::is_none")]
433 pub discriminator: Option<Discriminator>,
434
435 #[serde(skip_serializing_if = "Option::is_none", flatten)]
437 pub extensions: Option<Extensions>,
438 }
439}
440
441impl OneOf {
442 pub fn new() -> Self {
444 Self {
445 ..Default::default()
446 }
447 }
448
449 pub fn with_capacity(capacity: usize) -> Self {
462 Self {
463 items: Vec::with_capacity(capacity),
464 ..Default::default()
465 }
466 }
467}
468
469impl Default for OneOf {
470 fn default() -> Self {
471 Self {
472 items: Default::default(),
473 schema_type: SchemaType::AnyValue,
474 title: Default::default(),
475 description: Default::default(),
476 default: Default::default(),
477 example: Default::default(),
478 examples: Default::default(),
479 discriminator: Default::default(),
480 extensions: Default::default(),
481 }
482 }
483}
484
485impl OneOfBuilder {
486 pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
490 self.items.push(component.into());
491
492 self
493 }
494
495 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
498 set_value!(self schema_type schema_type.into())
499 }
500
501 pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
503 set_value!(self title title.map(|title| title.into()))
504 }
505
506 pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
508 set_value!(self description description.map(|description| description.into()))
509 }
510
511 pub fn default(mut self, default: Option<Value>) -> Self {
513 set_value!(self default default)
514 }
515
516 #[deprecated = "Since OpenAPI 3.1 prefer using `examples`"]
520 pub fn example(mut self, example: Option<Value>) -> Self {
521 set_value!(self example example)
522 }
523
524 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
526 set_value!(self examples examples.into_iter().map(Into::into).collect())
527 }
528
529 pub fn discriminator(mut self, discriminator: Option<Discriminator>) -> Self {
531 set_value!(self discriminator discriminator)
532 }
533
534 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
536 set_value!(self extensions extensions)
537 }
538
539 to_array_builder!();
540}
541
542impl From<OneOf> for Schema {
543 fn from(one_of: OneOf) -> Self {
544 Self::OneOf(one_of)
545 }
546}
547
548impl From<OneOfBuilder> for RefOr<Schema> {
549 fn from(one_of: OneOfBuilder) -> Self {
550 Self::T(Schema::OneOf(one_of.build()))
551 }
552}
553
554impl From<OneOfBuilder> for ArrayItems {
555 fn from(value: OneOfBuilder) -> Self {
556 Self::RefOrSchema(Box::new(value.into()))
557 }
558}
559
560component_from_builder!(OneOfBuilder);
561
562builder! {
563 AllOfBuilder;
564
565 #[derive(Serialize, Deserialize, Clone, PartialEq)]
572 #[cfg_attr(feature = "debug", derive(Debug))]
573 pub struct AllOf {
574 #[serde(rename = "allOf")]
576 pub items: Vec<RefOr<Schema>>,
577
578 #[serde(rename = "type", default = "SchemaType::any", skip_serializing_if = "SchemaType::is_any_value")]
583 pub schema_type: SchemaType,
584
585 #[serde(skip_serializing_if = "Option::is_none")]
587 pub title: Option<String>,
588
589 #[serde(skip_serializing_if = "Option::is_none")]
591 pub description: Option<String>,
592
593 #[serde(skip_serializing_if = "Option::is_none")]
595 pub default: Option<Value>,
596
597 #[serde(skip_serializing_if = "Option::is_none")]
601 pub example: Option<Value>,
602
603 #[serde(skip_serializing_if = "Vec::is_empty", default)]
605 pub examples: Vec<Value>,
606
607 #[serde(skip_serializing_if = "Option::is_none")]
610 pub discriminator: Option<Discriminator>,
611
612 #[serde(skip_serializing_if = "Option::is_none", flatten)]
614 pub extensions: Option<Extensions>,
615 }
616}
617
618impl AllOf {
619 pub fn new() -> Self {
621 Self {
622 ..Default::default()
623 }
624 }
625
626 pub fn with_capacity(capacity: usize) -> Self {
639 Self {
640 items: Vec::with_capacity(capacity),
641 ..Default::default()
642 }
643 }
644}
645
646impl Default for AllOf {
647 fn default() -> Self {
648 Self {
649 items: Default::default(),
650 schema_type: SchemaType::AnyValue,
651 title: Default::default(),
652 description: Default::default(),
653 default: Default::default(),
654 example: Default::default(),
655 examples: Default::default(),
656 discriminator: Default::default(),
657 extensions: Default::default(),
658 }
659 }
660}
661
662impl AllOfBuilder {
663 pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
667 self.items.push(component.into());
668
669 self
670 }
671
672 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
675 set_value!(self schema_type schema_type.into())
676 }
677
678 pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
680 set_value!(self title title.map(|title| title.into()))
681 }
682
683 pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
685 set_value!(self description description.map(|description| description.into()))
686 }
687
688 pub fn default(mut self, default: Option<Value>) -> Self {
690 set_value!(self default default)
691 }
692
693 #[deprecated = "Since OpenAPI 3.1 prefer using `examples`"]
697 pub fn example(mut self, example: Option<Value>) -> Self {
698 set_value!(self example example)
699 }
700
701 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
703 set_value!(self examples examples.into_iter().map(Into::into).collect())
704 }
705
706 pub fn discriminator(mut self, discriminator: Option<Discriminator>) -> Self {
708 set_value!(self discriminator discriminator)
709 }
710
711 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
713 set_value!(self extensions extensions)
714 }
715
716 to_array_builder!();
717}
718
719impl From<AllOf> for Schema {
720 fn from(one_of: AllOf) -> Self {
721 Self::AllOf(one_of)
722 }
723}
724
725impl From<AllOfBuilder> for RefOr<Schema> {
726 fn from(one_of: AllOfBuilder) -> Self {
727 Self::T(Schema::AllOf(one_of.build()))
728 }
729}
730
731impl From<AllOfBuilder> for ArrayItems {
732 fn from(value: AllOfBuilder) -> Self {
733 Self::RefOrSchema(Box::new(value.into()))
734 }
735}
736
737component_from_builder!(AllOfBuilder);
738
739builder! {
740 AnyOfBuilder;
741
742 #[derive(Serialize, Deserialize, Clone, PartialEq)]
749 #[cfg_attr(feature = "debug", derive(Debug))]
750 pub struct AnyOf {
751 #[serde(rename = "anyOf")]
753 pub items: Vec<RefOr<Schema>>,
754
755 #[serde(rename = "type", default = "SchemaType::any", skip_serializing_if = "SchemaType::is_any_value")]
760 pub schema_type: SchemaType,
761
762 #[serde(skip_serializing_if = "Option::is_none")]
764 pub description: Option<String>,
765
766 #[serde(skip_serializing_if = "Option::is_none")]
768 pub default: Option<Value>,
769
770 #[serde(skip_serializing_if = "Option::is_none")]
774 pub example: Option<Value>,
775
776 #[serde(skip_serializing_if = "Vec::is_empty", default)]
778 pub examples: Vec<Value>,
779
780 #[serde(skip_serializing_if = "Option::is_none")]
783 pub discriminator: Option<Discriminator>,
784
785 #[serde(skip_serializing_if = "Option::is_none", flatten)]
787 pub extensions: Option<Extensions>,
788 }
789}
790
791impl AnyOf {
792 pub fn new() -> Self {
794 Self {
795 ..Default::default()
796 }
797 }
798
799 pub fn with_capacity(capacity: usize) -> Self {
812 Self {
813 items: Vec::with_capacity(capacity),
814 ..Default::default()
815 }
816 }
817}
818
819impl Default for AnyOf {
820 fn default() -> Self {
821 Self {
822 items: Default::default(),
823 schema_type: SchemaType::AnyValue,
824 description: Default::default(),
825 default: Default::default(),
826 example: Default::default(),
827 examples: Default::default(),
828 discriminator: Default::default(),
829 extensions: Default::default(),
830 }
831 }
832}
833
834impl AnyOfBuilder {
835 pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
839 self.items.push(component.into());
840
841 self
842 }
843
844 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
847 set_value!(self schema_type schema_type.into())
848 }
849
850 pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
852 set_value!(self description description.map(|description| description.into()))
853 }
854
855 pub fn default(mut self, default: Option<Value>) -> Self {
857 set_value!(self default default)
858 }
859
860 #[deprecated = "Since OpenAPI 3.1 prefer using `examples`"]
864 pub fn example(mut self, example: Option<Value>) -> Self {
865 set_value!(self example example)
866 }
867
868 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
870 set_value!(self examples examples.into_iter().map(Into::into).collect())
871 }
872
873 pub fn discriminator(mut self, discriminator: Option<Discriminator>) -> Self {
875 set_value!(self discriminator discriminator)
876 }
877
878 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
880 set_value!(self extensions extensions)
881 }
882
883 to_array_builder!();
884}
885
886impl From<AnyOf> for Schema {
887 fn from(any_of: AnyOf) -> Self {
888 Self::AnyOf(any_of)
889 }
890}
891
892impl From<AnyOfBuilder> for RefOr<Schema> {
893 fn from(any_of: AnyOfBuilder) -> Self {
894 Self::T(Schema::AnyOf(any_of.build()))
895 }
896}
897
898impl From<AnyOfBuilder> for ArrayItems {
899 fn from(value: AnyOfBuilder) -> Self {
900 Self::RefOrSchema(Box::new(value.into()))
901 }
902}
903
904component_from_builder!(AnyOfBuilder);
905
906#[cfg(not(feature = "preserve_order"))]
907type ObjectPropertiesMap<K, V> = BTreeMap<K, V>;
908#[cfg(feature = "preserve_order")]
909type ObjectPropertiesMap<K, V> = indexmap::IndexMap<K, V>;
910
911builder! {
912 ObjectBuilder;
913
914 #[non_exhaustive]
921 #[derive(Serialize, Deserialize, Default, Clone, PartialEq)]
922 #[cfg_attr(feature = "debug", derive(Debug))]
923 #[serde(rename_all = "camelCase")]
924 pub struct Object {
925 #[serde(rename = "type", skip_serializing_if="SchemaType::is_any_value")]
928 pub schema_type: SchemaType,
929
930 #[serde(skip_serializing_if = "Option::is_none")]
932 pub title: Option<String>,
933
934 #[serde(skip_serializing_if = "Option::is_none")]
936 pub format: Option<SchemaFormat>,
937
938 #[serde(skip_serializing_if = "Option::is_none")]
940 pub description: Option<String>,
941
942 #[serde(skip_serializing_if = "Option::is_none")]
944 pub default: Option<Value>,
945
946 #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
948 pub enum_values: Option<Vec<Value>>,
949
950 #[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")]
952 pub required: Vec<String>,
953
954 #[serde(skip_serializing_if = "ObjectPropertiesMap::is_empty", default = "ObjectPropertiesMap::new")]
962 pub properties: ObjectPropertiesMap<String, RefOr<Schema>>,
963
964 #[serde(skip_serializing_if = "Option::is_none")]
966 pub additional_properties: Option<Box<AdditionalProperties<Schema>>>,
967
968 #[serde(skip_serializing_if = "Option::is_none")]
971 pub property_names: Option<Box<Schema>>,
972
973 #[serde(skip_serializing_if = "Option::is_none")]
975 pub deprecated: Option<Deprecated>,
976
977 #[serde(skip_serializing_if = "Option::is_none")]
981 pub example: Option<Value>,
982
983 #[serde(skip_serializing_if = "Vec::is_empty", default)]
985 pub examples: Vec<Value>,
986
987 #[serde(skip_serializing_if = "Option::is_none")]
989 pub write_only: Option<bool>,
990
991 #[serde(skip_serializing_if = "Option::is_none")]
993 pub read_only: Option<bool>,
994
995 #[serde(skip_serializing_if = "Option::is_none")]
997 pub xml: Option<Xml>,
998
999 #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
1002 pub multiple_of: Option<crate::utoipa::Number>,
1003
1004 #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
1007 pub maximum: Option<crate::utoipa::Number>,
1008
1009 #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
1012 pub minimum: Option<crate::utoipa::Number>,
1013
1014 #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
1017 pub exclusive_maximum: Option<crate::utoipa::Number>,
1018
1019 #[serde(skip_serializing_if = "Option::is_none", serialize_with = "omit_decimal_zero")]
1022 pub exclusive_minimum: Option<crate::utoipa::Number>,
1023
1024 #[serde(skip_serializing_if = "Option::is_none")]
1027 pub max_length: Option<usize>,
1028
1029 #[serde(skip_serializing_if = "Option::is_none")]
1033 pub min_length: Option<usize>,
1034
1035 #[serde(skip_serializing_if = "Option::is_none")]
1038 pub pattern: Option<String>,
1039
1040 #[serde(skip_serializing_if = "Option::is_none")]
1042 pub max_properties: Option<usize>,
1043
1044 #[serde(skip_serializing_if = "Option::is_none")]
1047 pub min_properties: Option<usize>,
1048
1049 #[serde(skip_serializing_if = "Option::is_none", flatten)]
1051 pub extensions: Option<Extensions>,
1052
1053 #[serde(skip_serializing_if = "String::is_empty", default)]
1062 pub content_encoding: String,
1063
1064 #[serde(skip_serializing_if = "String::is_empty", default)]
1069 pub content_media_type: String,
1070 }
1071}
1072
1073fn is_false(value: &bool) -> bool {
1074 !*value
1075}
1076
1077impl Object {
1078 pub fn new() -> Self {
1081 Self {
1082 ..Default::default()
1083 }
1084 }
1085
1086 pub fn with_type<T: Into<SchemaType>>(schema_type: T) -> Self {
1094 Self {
1095 schema_type: schema_type.into(),
1096 ..Default::default()
1097 }
1098 }
1099}
1100
1101impl From<Object> for Schema {
1102 fn from(s: Object) -> Self {
1103 Self::Object(s)
1104 }
1105}
1106
1107impl From<Object> for ArrayItems {
1108 fn from(value: Object) -> Self {
1109 Self::RefOrSchema(Box::new(value.into()))
1110 }
1111}
1112
1113impl ToArray for Object {}
1114
1115impl ObjectBuilder {
1116 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
1119 set_value!(self schema_type schema_type.into())
1120 }
1121
1122 pub fn format(mut self, format: Option<SchemaFormat>) -> Self {
1124 set_value!(self format format)
1125 }
1126
1127 pub fn property<S: Into<String>, I: Into<RefOr<Schema>>>(
1131 mut self,
1132 property_name: S,
1133 component: I,
1134 ) -> Self {
1135 self.properties
1136 .insert(property_name.into(), component.into());
1137
1138 self
1139 }
1140
1141 pub fn additional_properties<I: Into<AdditionalProperties<Schema>>>(
1143 mut self,
1144 additional_properties: Option<I>,
1145 ) -> Self {
1146 set_value!(self additional_properties additional_properties.map(|additional_properties| Box::new(additional_properties.into())))
1147 }
1148
1149 pub fn property_names<S: Into<Schema>>(mut self, property_name: Option<S>) -> Self {
1152 set_value!(self property_names property_name.map(|property_name| Box::new(property_name.into())))
1153 }
1154
1155 pub fn required<I: Into<String>>(mut self, required_field: I) -> Self {
1157 self.required.push(required_field.into());
1158
1159 self
1160 }
1161
1162 pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
1164 set_value!(self title title.map(|title| title.into()))
1165 }
1166
1167 pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
1169 set_value!(self description description.map(|description| description.into()))
1170 }
1171
1172 pub fn default(mut self, default: Option<Value>) -> Self {
1174 set_value!(self default default)
1175 }
1176
1177 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
1179 set_value!(self deprecated deprecated)
1180 }
1181
1182 pub fn enum_values<I: IntoIterator<Item = E>, E: Into<Value>>(
1184 mut self,
1185 enum_values: Option<I>,
1186 ) -> Self {
1187 set_value!(self enum_values
1188 enum_values.map(|values| values.into_iter().map(|enum_value| enum_value.into()).collect()))
1189 }
1190
1191 #[deprecated = "Since OpenAPI 3.1 prefer using `examples`"]
1195 pub fn example(mut self, example: Option<Value>) -> Self {
1196 set_value!(self example example)
1197 }
1198
1199 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
1201 set_value!(self examples examples.into_iter().map(Into::into).collect())
1202 }
1203
1204 pub fn write_only(mut self, write_only: Option<bool>) -> Self {
1206 set_value!(self write_only write_only)
1207 }
1208
1209 pub fn read_only(mut self, read_only: Option<bool>) -> Self {
1211 set_value!(self read_only read_only)
1212 }
1213
1214 pub fn xml(mut self, xml: Option<Xml>) -> Self {
1216 set_value!(self xml xml)
1217 }
1218
1219 pub fn multiple_of<N: Into<crate::utoipa::Number>>(mut self, multiple_of: Option<N>) -> Self {
1221 set_value!(self multiple_of multiple_of.map(|multiple_of| multiple_of.into()))
1222 }
1223
1224 pub fn maximum<N: Into<crate::utoipa::Number>>(mut self, maximum: Option<N>) -> Self {
1226 set_value!(self maximum maximum.map(|max| max.into()))
1227 }
1228
1229 pub fn minimum<N: Into<crate::utoipa::Number>>(mut self, minimum: Option<N>) -> Self {
1231 set_value!(self minimum minimum.map(|min| min.into()))
1232 }
1233
1234 pub fn exclusive_maximum<N: Into<crate::utoipa::Number>>(
1236 mut self,
1237 exclusive_maximum: Option<N>,
1238 ) -> Self {
1239 set_value!(self exclusive_maximum exclusive_maximum.map(|exclusive_maximum| exclusive_maximum.into()))
1240 }
1241
1242 pub fn exclusive_minimum<N: Into<crate::utoipa::Number>>(
1244 mut self,
1245 exclusive_minimum: Option<N>,
1246 ) -> Self {
1247 set_value!(self exclusive_minimum exclusive_minimum.map(|exclusive_minimum| exclusive_minimum.into()))
1248 }
1249
1250 pub fn max_length(mut self, max_length: Option<usize>) -> Self {
1252 set_value!(self max_length max_length)
1253 }
1254
1255 pub fn min_length(mut self, min_length: Option<usize>) -> Self {
1257 set_value!(self min_length min_length)
1258 }
1259
1260 pub fn pattern<I: Into<String>>(mut self, pattern: Option<I>) -> Self {
1262 set_value!(self pattern pattern.map(|pattern| pattern.into()))
1263 }
1264
1265 pub fn max_properties(mut self, max_properties: Option<usize>) -> Self {
1267 set_value!(self max_properties max_properties)
1268 }
1269
1270 pub fn min_properties(mut self, min_properties: Option<usize>) -> Self {
1272 set_value!(self min_properties min_properties)
1273 }
1274
1275 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
1277 set_value!(self extensions extensions)
1278 }
1279
1280 pub fn content_encoding<S: Into<String>>(mut self, content_encoding: S) -> Self {
1283 set_value!(self content_encoding content_encoding.into())
1284 }
1285
1286 pub fn content_media_type<S: Into<String>>(mut self, content_media_type: S) -> Self {
1289 set_value!(self content_media_type content_media_type.into())
1290 }
1291
1292 to_array_builder!();
1293}
1294
1295component_from_builder!(ObjectBuilder);
1296
1297impl From<ObjectBuilder> for RefOr<Schema> {
1298 fn from(builder: ObjectBuilder) -> Self {
1299 Self::T(Schema::Object(builder.build()))
1300 }
1301}
1302
1303impl From<RefOr<Schema>> for Schema {
1304 fn from(value: RefOr<Schema>) -> Self {
1305 match value {
1306 RefOr::Ref(_) => {
1307 panic!("Invalid type `RefOr::Ref` provided, cannot convert to RefOr::T<Schema>")
1308 }
1309 RefOr::T(value) => value,
1310 }
1311 }
1312}
1313
1314impl From<ObjectBuilder> for ArrayItems {
1315 fn from(value: ObjectBuilder) -> Self {
1316 Self::RefOrSchema(Box::new(value.into()))
1317 }
1318}
1319
1320#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1324#[cfg_attr(feature = "debug", derive(Debug))]
1325#[serde(untagged)]
1326pub enum AdditionalProperties<T> {
1327 RefOr(RefOr<T>),
1329 FreeForm(bool),
1331}
1332
1333impl<T> From<RefOr<T>> for AdditionalProperties<T> {
1334 fn from(value: RefOr<T>) -> Self {
1335 Self::RefOr(value)
1336 }
1337}
1338
1339impl From<ObjectBuilder> for AdditionalProperties<Schema> {
1340 fn from(value: ObjectBuilder) -> Self {
1341 Self::RefOr(RefOr::T(Schema::Object(value.build())))
1342 }
1343}
1344
1345impl From<ArrayBuilder> for AdditionalProperties<Schema> {
1346 fn from(value: ArrayBuilder) -> Self {
1347 Self::RefOr(RefOr::T(Schema::Array(value.build())))
1348 }
1349}
1350
1351impl From<Ref> for AdditionalProperties<Schema> {
1352 fn from(value: Ref) -> Self {
1353 Self::RefOr(RefOr::Ref(value))
1354 }
1355}
1356
1357impl From<RefBuilder> for AdditionalProperties<Schema> {
1358 fn from(value: RefBuilder) -> Self {
1359 Self::RefOr(RefOr::Ref(value.build()))
1360 }
1361}
1362
1363impl From<Schema> for AdditionalProperties<Schema> {
1364 fn from(value: Schema) -> Self {
1365 Self::RefOr(RefOr::T(value))
1366 }
1367}
1368
1369impl From<AllOfBuilder> for AdditionalProperties<Schema> {
1370 fn from(value: AllOfBuilder) -> Self {
1371 Self::RefOr(RefOr::T(Schema::AllOf(value.build())))
1372 }
1373}
1374
1375builder! {
1376 RefBuilder;
1377
1378 #[non_exhaustive]
1383 #[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
1384 #[cfg_attr(feature = "debug", derive(Debug))]
1385 pub struct Ref {
1386 #[serde(rename = "$ref")]
1388 pub ref_location: String,
1389
1390 #[serde(skip_serializing_if = "String::is_empty", default)]
1394 pub description: String,
1395
1396 #[serde(skip_serializing_if = "String::is_empty", default)]
1399 pub summary: String,
1400 }
1401}
1402
1403impl Ref {
1404 pub fn new<I: Into<String>>(ref_location: I) -> Self {
1407 Self {
1408 ref_location: ref_location.into(),
1409 ..Default::default()
1410 }
1411 }
1412
1413 pub fn from_schema_name<I: Into<String>>(schema_name: I) -> Self {
1416 Self::new(format!("#/components/schemas/{}", schema_name.into()))
1417 }
1418
1419 pub fn from_response_name<I: Into<String>>(response_name: I) -> Self {
1422 Self::new(format!("#/components/responses/{}", response_name.into()))
1423 }
1424
1425 to_array_builder!();
1426}
1427
1428impl RefBuilder {
1429 pub fn ref_location(mut self, ref_location: String) -> Self {
1431 set_value!(self ref_location ref_location)
1432 }
1433
1434 pub fn ref_location_from_schema_name<S: Into<String>>(mut self, schema_name: S) -> Self {
1437 set_value!(self ref_location format!("#/components/schemas/{}", schema_name.into()))
1438 }
1439
1440 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
1446 set_value!(self description description.map(Into::into).unwrap_or_default())
1447 }
1448
1449 pub fn summary<S: Into<String>>(mut self, summary: S) -> Self {
1452 set_value!(self summary summary.into())
1453 }
1454}
1455
1456impl From<RefBuilder> for RefOr<Schema> {
1457 fn from(builder: RefBuilder) -> Self {
1458 Self::Ref(builder.build())
1459 }
1460}
1461
1462impl From<RefBuilder> for ArrayItems {
1463 fn from(value: RefBuilder) -> Self {
1464 Self::RefOrSchema(Box::new(value.into()))
1465 }
1466}
1467
1468impl From<Ref> for RefOr<Schema> {
1469 fn from(r: Ref) -> Self {
1470 Self::Ref(r)
1471 }
1472}
1473
1474impl From<Ref> for ArrayItems {
1475 fn from(value: Ref) -> Self {
1476 Self::RefOrSchema(Box::new(value.into()))
1477 }
1478}
1479
1480impl<T> From<T> for RefOr<T> {
1481 fn from(t: T) -> Self {
1482 Self::T(t)
1483 }
1484}
1485
1486impl Default for RefOr<Schema> {
1487 fn default() -> Self {
1488 Self::T(Schema::Object(Object::new()))
1489 }
1490}
1491
1492impl ToArray for RefOr<Schema> {}
1493
1494impl From<Object> for RefOr<Schema> {
1495 fn from(object: Object) -> Self {
1496 Self::T(Schema::Object(object))
1497 }
1498}
1499
1500impl From<Array> for RefOr<Schema> {
1501 fn from(array: Array) -> Self {
1502 Self::T(Schema::Array(array))
1503 }
1504}
1505
1506fn omit_decimal_zero<S>(
1507 maybe_value: &Option<crate::utoipa::Number>,
1508 serializer: S,
1509) -> Result<S::Ok, S::Error>
1510where
1511 S: serde::Serializer,
1512{
1513 match maybe_value {
1514 Some(crate::utoipa::Number::Float(float)) => {
1515 if float.fract() == 0.0 && *float >= i64::MIN as f64 && *float <= i64::MAX as f64 {
1516 serializer.serialize_i64(float.trunc() as i64)
1517 } else {
1518 serializer.serialize_f64(*float)
1519 }
1520 }
1521 Some(crate::utoipa::Number::Int(int)) => serializer.serialize_i64(*int as i64),
1522 Some(crate::utoipa::Number::UInt(uint)) => serializer.serialize_u64(*uint as u64),
1523 None => serializer.serialize_none(),
1524 }
1525}
1526
1527#[derive(Serialize, Deserialize, Clone, PartialEq)]
1531#[cfg_attr(feature = "debug", derive(Debug))]
1532#[serde(untagged)]
1533pub enum ArrayItems {
1534 RefOrSchema(Box<RefOr<Schema>>),
1536 #[serde(with = "array_items_false")]
1542 False,
1543}
1544
1545mod array_items_false {
1546 use serde::de::Visitor;
1547
1548 pub fn serialize<S: serde::Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
1549 serializer.serialize_bool(false)
1550 }
1551
1552 pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<(), D::Error> {
1553 struct ItemsFalseVisitor;
1554
1555 impl<'de> Visitor<'de> for ItemsFalseVisitor {
1556 type Value = ();
1557 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
1558 where
1559 E: serde::de::Error,
1560 {
1561 if !v {
1562 Ok(())
1563 } else {
1564 Err(serde::de::Error::custom(format!(
1565 "invalid boolean value: {v}, expected false"
1566 )))
1567 }
1568 }
1569
1570 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1571 formatter.write_str("expected boolean false")
1572 }
1573 }
1574
1575 deserializer.deserialize_bool(ItemsFalseVisitor)
1576 }
1577}
1578
1579impl Default for ArrayItems {
1580 fn default() -> Self {
1581 Self::RefOrSchema(Box::new(Object::with_type(SchemaType::AnyValue).into()))
1582 }
1583}
1584
1585impl From<RefOr<Schema>> for ArrayItems {
1586 fn from(value: RefOr<Schema>) -> Self {
1587 Self::RefOrSchema(Box::new(value))
1588 }
1589}
1590
1591builder! {
1592 ArrayBuilder;
1593
1594 #[non_exhaustive]
1598 #[derive(Serialize, Deserialize, Clone, PartialEq)]
1599 #[cfg_attr(feature = "debug", derive(Debug))]
1600 #[serde(rename_all = "camelCase")]
1601 pub struct Array {
1602 #[serde(rename = "type")]
1604 pub schema_type: SchemaType,
1605
1606 #[serde(skip_serializing_if = "Option::is_none")]
1608 pub title: Option<String>,
1609
1610 pub items: ArrayItems,
1612
1613 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1618 pub prefix_items: Vec<Schema>,
1619
1620 #[serde(skip_serializing_if = "Option::is_none")]
1622 pub description: Option<String>,
1623
1624 #[serde(skip_serializing_if = "Option::is_none")]
1626 pub deprecated: Option<Deprecated>,
1627
1628 #[serde(skip_serializing_if = "Option::is_none")]
1632 pub example: Option<Value>,
1633
1634 #[serde(skip_serializing_if = "Vec::is_empty", default)]
1636 pub examples: Vec<Value>,
1637
1638 #[serde(skip_serializing_if = "Option::is_none")]
1640 pub default: Option<Value>,
1641
1642 #[serde(skip_serializing_if = "Option::is_none")]
1644 pub max_items: Option<usize>,
1645
1646 #[serde(skip_serializing_if = "Option::is_none")]
1648 pub min_items: Option<usize>,
1649
1650 #[serde(default, skip_serializing_if = "is_false")]
1653 pub unique_items: bool,
1654
1655 #[serde(skip_serializing_if = "Option::is_none")]
1657 pub xml: Option<Xml>,
1658
1659 #[serde(skip_serializing_if = "String::is_empty", default)]
1668 pub content_encoding: String,
1669
1670 #[serde(skip_serializing_if = "String::is_empty", default)]
1675 pub content_media_type: String,
1676
1677 #[serde(skip_serializing_if = "Option::is_none", flatten)]
1679 pub extensions: Option<Extensions>,
1680 }
1681}
1682
1683impl Default for Array {
1684 fn default() -> Self {
1685 Self {
1686 title: Default::default(),
1687 schema_type: Type::Array.into(),
1688 unique_items: bool::default(),
1689 items: Default::default(),
1690 prefix_items: Vec::default(),
1691 description: Default::default(),
1692 deprecated: Default::default(),
1693 example: Default::default(),
1694 examples: Default::default(),
1695 default: Default::default(),
1696 max_items: Default::default(),
1697 min_items: Default::default(),
1698 xml: Default::default(),
1699 extensions: Default::default(),
1700 content_encoding: Default::default(),
1701 content_media_type: Default::default(),
1702 }
1703 }
1704}
1705
1706impl Array {
1707 pub fn new<I: Into<RefOr<Schema>>>(component: I) -> Self {
1717 Self {
1718 items: ArrayItems::RefOrSchema(Box::new(component.into())),
1719 ..Default::default()
1720 }
1721 }
1722
1723 pub fn new_nullable<I: Into<RefOr<Schema>>>(component: I) -> Self {
1733 Self {
1734 items: ArrayItems::RefOrSchema(Box::new(component.into())),
1735 schema_type: SchemaType::from_iter([Type::Array, Type::Null]),
1736 ..Default::default()
1737 }
1738 }
1739}
1740
1741impl ArrayBuilder {
1742 pub fn items<I: Into<ArrayItems>>(mut self, items: I) -> Self {
1744 set_value!(self items items.into())
1745 }
1746
1747 pub fn prefix_items<I: IntoIterator<Item = S>, S: Into<Schema>>(mut self, items: I) -> Self {
1752 self.prefix_items = items
1753 .into_iter()
1754 .map(|item| item.into())
1755 .collect::<Vec<_>>();
1756
1757 self
1758 }
1759
1760 pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
1774 set_value!(self schema_type schema_type.into())
1775 }
1776
1777 pub fn title<I: Into<String>>(mut self, title: Option<I>) -> Self {
1779 set_value!(self title title.map(|title| title.into()))
1780 }
1781
1782 pub fn description<I: Into<String>>(mut self, description: Option<I>) -> Self {
1784 set_value!(self description description.map(|description| description.into()))
1785 }
1786
1787 pub fn deprecated(mut self, deprecated: Option<Deprecated>) -> Self {
1789 set_value!(self deprecated deprecated)
1790 }
1791
1792 #[deprecated = "Since OpenAPI 3.1 prefer using `examples`"]
1796 pub fn example(mut self, example: Option<Value>) -> Self {
1797 set_value!(self example example)
1798 }
1799
1800 pub fn examples<I: IntoIterator<Item = V>, V: Into<Value>>(mut self, examples: I) -> Self {
1802 set_value!(self examples examples.into_iter().map(Into::into).collect())
1803 }
1804
1805 pub fn default(mut self, default: Option<Value>) -> Self {
1807 set_value!(self default default)
1808 }
1809
1810 pub fn max_items(mut self, max_items: Option<usize>) -> Self {
1812 set_value!(self max_items max_items)
1813 }
1814
1815 pub fn min_items(mut self, min_items: Option<usize>) -> Self {
1817 set_value!(self min_items min_items)
1818 }
1819
1820 pub fn unique_items(mut self, unique_items: bool) -> Self {
1822 set_value!(self unique_items unique_items)
1823 }
1824
1825 pub fn xml(mut self, xml: Option<Xml>) -> Self {
1827 set_value!(self xml xml)
1828 }
1829
1830 pub fn content_encoding<S: Into<String>>(mut self, content_encoding: S) -> Self {
1833 set_value!(self content_encoding content_encoding.into())
1834 }
1835
1836 pub fn content_media_type<S: Into<String>>(mut self, content_media_type: S) -> Self {
1839 set_value!(self content_media_type content_media_type.into())
1840 }
1841
1842 pub fn extensions(mut self, extensions: Option<Extensions>) -> Self {
1844 set_value!(self extensions extensions)
1845 }
1846
1847 to_array_builder!();
1848}
1849
1850component_from_builder!(ArrayBuilder);
1851
1852impl From<Array> for Schema {
1853 fn from(array: Array) -> Self {
1854 Self::Array(array)
1855 }
1856}
1857
1858impl From<ArrayBuilder> for ArrayItems {
1859 fn from(value: ArrayBuilder) -> Self {
1860 Self::RefOrSchema(Box::new(value.into()))
1861 }
1862}
1863
1864impl From<ArrayBuilder> for RefOr<Schema> {
1865 fn from(array: ArrayBuilder) -> Self {
1866 Self::T(Schema::Array(array.build()))
1867 }
1868}
1869
1870impl ToArray for Array {}
1871
1872pub trait ToArray
1874where
1875 RefOr<Schema>: From<Self>,
1876 Self: Sized,
1877{
1878 fn to_array(self) -> Array {
1880 Array::new(self)
1881 }
1882}
1883
1884#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
1889#[cfg_attr(feature = "debug", derive(Debug))]
1890#[serde(untagged)]
1891pub enum SchemaType {
1892 Type(Type),
1894 Array(Vec<Type>),
1896 AnyValue,
1899}
1900
1901impl Default for SchemaType {
1902 fn default() -> Self {
1903 Self::Type(Type::default())
1904 }
1905}
1906
1907impl From<Type> for SchemaType {
1908 fn from(value: Type) -> Self {
1909 SchemaType::new(value)
1910 }
1911}
1912
1913impl FromIterator<Type> for SchemaType {
1914 fn from_iter<T: IntoIterator<Item = Type>>(iter: T) -> Self {
1915 Self::Array(iter.into_iter().collect())
1916 }
1917}
1918
1919impl SchemaType {
1920 pub fn new(r#type: Type) -> Self {
1932 Self::Type(r#type)
1933 }
1934
1935 pub fn any() -> Self {
1940 SchemaType::AnyValue
1941 }
1942
1943 pub fn is_any_value(&self) -> bool {
1946 matches!(self, Self::AnyValue)
1947 }
1948}
1949
1950#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
1970#[cfg_attr(feature = "debug", derive(Debug))]
1971#[serde(rename_all = "lowercase")]
1972pub enum Type {
1973 #[default]
1976 Object,
1977 String,
1980 Integer,
1983 Number,
1986 Boolean,
1989 Array,
1991 Null,
1993}
1994
1995#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
2000#[cfg_attr(feature = "debug", derive(Debug))]
2001#[serde(rename_all = "lowercase", untagged)]
2002pub enum SchemaFormat {
2003 KnownFormat(KnownFormat),
2005 Custom(String),
2008}
2009
2010#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
2016#[cfg_attr(feature = "debug", derive(Debug))]
2017#[serde(rename_all = "kebab-case")]
2018pub enum KnownFormat {
2019 #[cfg(feature = "non_strict_integers")]
2021 #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
2022 Int8,
2023 #[cfg(feature = "non_strict_integers")]
2025 #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
2026 Int16,
2027 Int32,
2029 Int64,
2031 #[cfg(feature = "non_strict_integers")]
2033 #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
2034 UInt8,
2035 #[cfg(feature = "non_strict_integers")]
2037 #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
2038 UInt16,
2039 #[cfg(feature = "non_strict_integers")]
2041 #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
2042 UInt32,
2043 #[cfg(feature = "non_strict_integers")]
2045 #[cfg_attr(doc_cfg, doc(cfg(feature = "non_strict_integers")))]
2046 UInt64,
2047 Float,
2049 Double,
2051 Byte,
2053 Binary,
2055 Time,
2057 Date,
2059 DateTime,
2061 Duration,
2063 Password,
2065 #[cfg(feature = "uuid")]
2069 #[cfg_attr(doc_cfg, doc(cfg(feature = "uuid")))]
2070 Uuid,
2071 #[cfg(feature = "ulid")]
2073 #[cfg_attr(doc_cfg, doc(cfg(feature = "ulid")))]
2074 Ulid,
2075 #[cfg(feature = "url")]
2078 #[cfg_attr(doc_cfg, doc(cfg(feature = "url")))]
2079 Uri,
2080 #[cfg(feature = "url")]
2084 #[cfg_attr(doc_cfg, doc(cfg(feature = "url")))]
2085 UriReference,
2086 #[cfg(feature = "url")]
2089 #[cfg_attr(doc_cfg, doc(cfg(feature = "url")))]
2090 Iri,
2091 #[cfg(feature = "url")]
2095 #[cfg_attr(doc_cfg, doc(cfg(feature = "url")))]
2096 IriReference,
2097 Email,
2099 IdnEmail,
2101 Hostname,
2105 IdnHostname,
2108 Ipv4,
2110 Ipv6,
2112 UriTemplate,
2117 JsonPointer,
2119 RelativeJsonPointer,
2121 Regex,
2124}
2125
2126#[cfg(test)]
2127mod tests {
2128 use insta::assert_json_snapshot;
2129 use serde_json::{json, Value};
2130
2131 use super::*;
2132 use crate::openapi::*;
2133
2134 #[test]
2135 fn create_schema_serializes_json() -> Result<(), serde_json::Error> {
2136 let openapi = OpenApiBuilder::new()
2137 .info(Info::new("My api", "1.0.0"))
2138 .paths(Paths::new())
2139 .components(Some(
2140 ComponentsBuilder::new()
2141 .schema("Person", Ref::new("#/components/PersonModel"))
2142 .schema(
2143 "Credential",
2144 Schema::from(
2145 ObjectBuilder::new()
2146 .property(
2147 "id",
2148 ObjectBuilder::new()
2149 .schema_type(Type::Integer)
2150 .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
2151 .description(Some("Id of credential"))
2152 .default(Some(json!(1i32))),
2153 )
2154 .property(
2155 "name",
2156 ObjectBuilder::new()
2157 .schema_type(Type::String)
2158 .description(Some("Name of credential")),
2159 )
2160 .property(
2161 "status",
2162 ObjectBuilder::new()
2163 .schema_type(Type::String)
2164 .default(Some(json!("Active")))
2165 .description(Some("Credential status"))
2166 .enum_values(Some([
2167 "Active",
2168 "NotActive",
2169 "Locked",
2170 "Expired",
2171 ])),
2172 )
2173 .property(
2174 "history",
2175 Array::new(Ref::from_schema_name("UpdateHistory")),
2176 )
2177 .property("tags", Object::with_type(Type::String).to_array()),
2178 ),
2179 )
2180 .build(),
2181 ))
2182 .build();
2183
2184 let serialized = serde_json::to_string_pretty(&openapi)?;
2185 println!("serialized json:\n {serialized}");
2186
2187 let value = serde_json::to_value(&openapi)?;
2188 let credential = get_json_path(&value, "components.schemas.Credential.properties");
2189 let person = get_json_path(&value, "components.schemas.Person");
2190
2191 assert!(
2192 credential.get("id").is_some(),
2193 "could not find path: components.schemas.Credential.properties.id"
2194 );
2195 assert!(
2196 credential.get("status").is_some(),
2197 "could not find path: components.schemas.Credential.properties.status"
2198 );
2199 assert!(
2200 credential.get("name").is_some(),
2201 "could not find path: components.schemas.Credential.properties.name"
2202 );
2203 assert!(
2204 credential.get("history").is_some(),
2205 "could not find path: components.schemas.Credential.properties.history"
2206 );
2207 assert_eq!(
2208 credential
2209 .get("id")
2210 .unwrap_or(&serde_json::value::Value::Null)
2211 .to_string(),
2212 r#"{"default":1,"description":"Id of credential","format":"int32","type":"integer"}"#,
2213 "components.schemas.Credential.properties.id did not match"
2214 );
2215 assert_eq!(
2216 credential
2217 .get("name")
2218 .unwrap_or(&serde_json::value::Value::Null)
2219 .to_string(),
2220 r#"{"description":"Name of credential","type":"string"}"#,
2221 "components.schemas.Credential.properties.name did not match"
2222 );
2223 assert_eq!(
2224 credential
2225 .get("status")
2226 .unwrap_or(&serde_json::value::Value::Null)
2227 .to_string(),
2228 r#"{"default":"Active","description":"Credential status","enum":["Active","NotActive","Locked","Expired"],"type":"string"}"#,
2229 "components.schemas.Credential.properties.status did not match"
2230 );
2231 assert_eq!(
2232 credential
2233 .get("history")
2234 .unwrap_or(&serde_json::value::Value::Null)
2235 .to_string(),
2236 r###"{"items":{"$ref":"#/components/schemas/UpdateHistory"},"type":"array"}"###,
2237 "components.schemas.Credential.properties.history did not match"
2238 );
2239 assert_eq!(
2240 person.to_string(),
2241 r###"{"$ref":"#/components/PersonModel"}"###,
2242 "components.schemas.Person.ref did not match"
2243 );
2244
2245 Ok(())
2246 }
2247
2248 #[test]
2250 fn test_property_order() {
2251 let json_value = ObjectBuilder::new()
2252 .property(
2253 "id",
2254 ObjectBuilder::new()
2255 .schema_type(Type::Integer)
2256 .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
2257 .description(Some("Id of credential"))
2258 .default(Some(json!(1i32))),
2259 )
2260 .property(
2261 "name",
2262 ObjectBuilder::new()
2263 .schema_type(Type::String)
2264 .description(Some("Name of credential")),
2265 )
2266 .property(
2267 "status",
2268 ObjectBuilder::new()
2269 .schema_type(Type::String)
2270 .default(Some(json!("Active")))
2271 .description(Some("Credential status"))
2272 .enum_values(Some(["Active", "NotActive", "Locked", "Expired"])),
2273 )
2274 .property(
2275 "history",
2276 Array::new(Ref::from_schema_name("UpdateHistory")),
2277 )
2278 .property("tags", Object::with_type(Type::String).to_array())
2279 .build();
2280
2281 #[cfg(not(feature = "preserve_order"))]
2282 assert_eq!(
2283 json_value.properties.keys().collect::<Vec<_>>(),
2284 vec!["history", "id", "name", "status", "tags"]
2285 );
2286
2287 #[cfg(feature = "preserve_order")]
2288 assert_eq!(
2289 json_value.properties.keys().collect::<Vec<_>>(),
2290 vec!["id", "name", "status", "history", "tags"]
2291 );
2292 }
2293
2294 #[test]
2296 fn test_additional_properties() {
2297 let json_value = ObjectBuilder::new()
2298 .additional_properties(Some(ObjectBuilder::new().schema_type(Type::String)))
2299 .build();
2300 assert_json_snapshot!(json_value, @r#"
2301 {
2302 "type": "object",
2303 "additionalProperties": {
2304 "type": "string"
2305 }
2306 }
2307 "#);
2308
2309 let json_value = ObjectBuilder::new()
2310 .additional_properties(Some(ArrayBuilder::new().items(ArrayItems::RefOrSchema(
2311 Box::new(ObjectBuilder::new().schema_type(Type::Number).into()),
2312 ))))
2313 .build();
2314 assert_json_snapshot!(json_value, @r#"
2315 {
2316 "type": "object",
2317 "additionalProperties": {
2318 "type": "array",
2319 "items": {
2320 "type": "number"
2321 }
2322 }
2323 }
2324 "#);
2325
2326 let json_value = ObjectBuilder::new()
2327 .additional_properties(Some(Ref::from_schema_name("ComplexModel")))
2328 .build();
2329 assert_json_snapshot!(json_value, @r##"
2330 {
2331 "type": "object",
2332 "additionalProperties": {
2333 "$ref": "#/components/schemas/ComplexModel"
2334 }
2335 }
2336 "##);
2337 }
2338
2339 #[test]
2340 fn test_object_with_title() {
2341 let json_value = ObjectBuilder::new().title(Some("SomeName")).build();
2342 assert_json_snapshot!(json_value, @r#"
2343 {
2344 "type": "object",
2345 "title": "SomeName"
2346 }
2347 "#);
2348 }
2349
2350 #[test]
2351 fn derive_object_with_examples() {
2352 let json_value = ObjectBuilder::new()
2353 .examples([Some(json!({"age": 20, "name": "bob the cat"}))])
2354 .build();
2355 assert_json_snapshot!(json_value, @r#"
2356 {
2357 "type": "object",
2358 "examples": [
2359 {
2360 "age": 20,
2361 "name": "bob the cat"
2362 }
2363 ]
2364 }
2365 "#);
2366 }
2367
2368 fn get_json_path<'a>(value: &'a Value, path: &str) -> &'a Value {
2369 path.split('.').fold(value, |acc, fragment| {
2370 acc.get(fragment).unwrap_or(&serde_json::value::Value::Null)
2371 })
2372 }
2373
2374 #[test]
2375 fn test_array_new() {
2376 let array = Array::new(
2377 ObjectBuilder::new().property(
2378 "id",
2379 ObjectBuilder::new()
2380 .schema_type(Type::Integer)
2381 .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
2382 .description(Some("Id of credential"))
2383 .default(Some(json!(1i32))),
2384 ),
2385 );
2386
2387 assert!(matches!(array.schema_type, SchemaType::Type(Type::Array)));
2388 }
2389
2390 #[test]
2391 fn test_array_builder() {
2392 let array: Array = ArrayBuilder::new()
2393 .items(
2394 ObjectBuilder::new().property(
2395 "id",
2396 ObjectBuilder::new()
2397 .schema_type(Type::Integer)
2398 .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
2399 .description(Some("Id of credential"))
2400 .default(Some(json!(1i32))),
2401 ),
2402 )
2403 .build();
2404
2405 assert!(matches!(array.schema_type, SchemaType::Type(Type::Array)));
2406 }
2407
2408 #[test]
2409 fn reserialize_deserialized_schema_components() {
2410 let components = ComponentsBuilder::new()
2411 .schemas_from_iter(vec![(
2412 "Comp",
2413 Schema::from(
2414 ObjectBuilder::new()
2415 .property("name", ObjectBuilder::new().schema_type(Type::String))
2416 .required("name"),
2417 ),
2418 )])
2419 .responses_from_iter(vec![(
2420 "200",
2421 ResponseBuilder::new().description("Okay").build(),
2422 )])
2423 .security_scheme(
2424 "TLS",
2425 SecurityScheme::MutualTls {
2426 description: None,
2427 extensions: None,
2428 },
2429 )
2430 .build();
2431
2432 let serialized_components = serde_json::to_string(&components).unwrap();
2433
2434 let deserialized_components: Components =
2435 serde_json::from_str(serialized_components.as_str()).unwrap();
2436
2437 assert_eq!(
2438 serialized_components,
2439 serde_json::to_string(&deserialized_components).unwrap()
2440 )
2441 }
2442
2443 #[test]
2444 fn reserialize_deserialized_object_component() {
2445 let prop = ObjectBuilder::new()
2446 .property("name", ObjectBuilder::new().schema_type(Type::String))
2447 .required("name")
2448 .build();
2449
2450 let serialized_components = serde_json::to_string(&prop).unwrap();
2451 let deserialized_components: Object =
2452 serde_json::from_str(serialized_components.as_str()).unwrap();
2453
2454 assert_eq!(
2455 serialized_components,
2456 serde_json::to_string(&deserialized_components).unwrap()
2457 )
2458 }
2459
2460 #[test]
2461 fn reserialize_deserialized_property() {
2462 let prop = ObjectBuilder::new().schema_type(Type::String).build();
2463
2464 let serialized_components = serde_json::to_string(&prop).unwrap();
2465 let deserialized_components: Object =
2466 serde_json::from_str(serialized_components.as_str()).unwrap();
2467
2468 assert_eq!(
2469 serialized_components,
2470 serde_json::to_string(&deserialized_components).unwrap()
2471 )
2472 }
2473
2474 #[test]
2475 fn serialize_deserialize_array_within_ref_or_t_object_builder() {
2476 let ref_or_schema = RefOr::T(Schema::Object(
2477 ObjectBuilder::new()
2478 .property(
2479 "test",
2480 RefOr::T(Schema::Array(
2481 ArrayBuilder::new()
2482 .items(RefOr::T(Schema::Object(
2483 ObjectBuilder::new()
2484 .property("element", RefOr::Ref(Ref::new("#/test")))
2485 .build(),
2486 )))
2487 .build(),
2488 )),
2489 )
2490 .build(),
2491 ));
2492
2493 let json_str = serde_json::to_string(&ref_or_schema).expect("");
2494 println!("----------------------------");
2495 println!("{json_str}");
2496
2497 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
2498
2499 let json_de_str = serde_json::to_string(&deserialized).expect("");
2500 println!("----------------------------");
2501 println!("{json_de_str}");
2502
2503 assert_eq!(json_str, json_de_str);
2504 }
2505
2506 #[test]
2507 fn serialize_deserialize_one_of_within_ref_or_t_object_builder() {
2508 let ref_or_schema = RefOr::T(Schema::Object(
2509 ObjectBuilder::new()
2510 .property(
2511 "test",
2512 RefOr::T(Schema::OneOf(
2513 OneOfBuilder::new()
2514 .item(Schema::Array(
2515 ArrayBuilder::new()
2516 .items(RefOr::T(Schema::Object(
2517 ObjectBuilder::new()
2518 .property("element", RefOr::Ref(Ref::new("#/test")))
2519 .build(),
2520 )))
2521 .build(),
2522 ))
2523 .item(Schema::Array(
2524 ArrayBuilder::new()
2525 .items(RefOr::T(Schema::Object(
2526 ObjectBuilder::new()
2527 .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
2528 .build(),
2529 )))
2530 .build(),
2531 ))
2532 .build(),
2533 )),
2534 )
2535 .build(),
2536 ));
2537
2538 let json_str = serde_json::to_string(&ref_or_schema).expect("");
2539 println!("----------------------------");
2540 println!("{json_str}");
2541
2542 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
2543
2544 let json_de_str = serde_json::to_string(&deserialized).expect("");
2545 println!("----------------------------");
2546 println!("{json_de_str}");
2547
2548 assert_eq!(json_str, json_de_str);
2549 }
2550
2551 #[test]
2552 fn serialize_deserialize_all_of_of_within_ref_or_t_object_builder() {
2553 let ref_or_schema = RefOr::T(Schema::Object(
2554 ObjectBuilder::new()
2555 .property(
2556 "test",
2557 RefOr::T(Schema::AllOf(
2558 AllOfBuilder::new()
2559 .item(Schema::Array(
2560 ArrayBuilder::new()
2561 .items(RefOr::T(Schema::Object(
2562 ObjectBuilder::new()
2563 .property("element", RefOr::Ref(Ref::new("#/test")))
2564 .build(),
2565 )))
2566 .build(),
2567 ))
2568 .item(RefOr::T(Schema::Object(
2569 ObjectBuilder::new()
2570 .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
2571 .build(),
2572 )))
2573 .build(),
2574 )),
2575 )
2576 .build(),
2577 ));
2578
2579 let json_str = serde_json::to_string(&ref_or_schema).expect("");
2580 println!("----------------------------");
2581 println!("{json_str}");
2582
2583 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
2584
2585 let json_de_str = serde_json::to_string(&deserialized).expect("");
2586 println!("----------------------------");
2587 println!("{json_de_str}");
2588
2589 assert_eq!(json_str, json_de_str);
2590 }
2591
2592 #[test]
2593 fn deserialize_reserialize_one_of_default_type() {
2594 let a = OneOfBuilder::new()
2595 .item(Schema::Array(
2596 ArrayBuilder::new()
2597 .items(RefOr::T(Schema::Object(
2598 ObjectBuilder::new()
2599 .property("element", RefOr::Ref(Ref::new("#/test")))
2600 .build(),
2601 )))
2602 .build(),
2603 ))
2604 .item(Schema::Array(
2605 ArrayBuilder::new()
2606 .items(RefOr::T(Schema::Object(
2607 ObjectBuilder::new()
2608 .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
2609 .build(),
2610 )))
2611 .build(),
2612 ))
2613 .build();
2614
2615 let serialized_json = serde_json::to_string(&a).expect("should serialize to json");
2616 let b: OneOf = serde_json::from_str(&serialized_json).expect("should deserialize OneOf");
2617 let reserialized_json = serde_json::to_string(&b).expect("reserialized json");
2618
2619 println!("{serialized_json}");
2620 println!("{reserialized_json}",);
2621 assert_eq!(serialized_json, reserialized_json);
2622 }
2623
2624 #[test]
2625 fn serialize_deserialize_any_of_of_within_ref_or_t_object_builder() {
2626 let ref_or_schema = RefOr::T(Schema::Object(
2627 ObjectBuilder::new()
2628 .property(
2629 "test",
2630 RefOr::T(Schema::AnyOf(
2631 AnyOfBuilder::new()
2632 .item(Schema::Array(
2633 ArrayBuilder::new()
2634 .items(RefOr::T(Schema::Object(
2635 ObjectBuilder::new()
2636 .property("element", RefOr::Ref(Ref::new("#/test")))
2637 .build(),
2638 )))
2639 .build(),
2640 ))
2641 .item(RefOr::T(Schema::Object(
2642 ObjectBuilder::new()
2643 .property("foobar", RefOr::Ref(Ref::new("#/foobar")))
2644 .build(),
2645 )))
2646 .build(),
2647 )),
2648 )
2649 .build(),
2650 ));
2651
2652 let json_str = serde_json::to_string(&ref_or_schema).expect("");
2653 println!("----------------------------");
2654 println!("{json_str}");
2655
2656 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
2657
2658 let json_de_str = serde_json::to_string(&deserialized).expect("");
2659 println!("----------------------------");
2660 println!("{json_de_str}");
2661 assert!(json_str.contains("\"anyOf\""));
2662 assert_eq!(json_str, json_de_str);
2663 }
2664
2665 #[test]
2666 fn serialize_deserialize_schema_array_ref_or_t() {
2667 let ref_or_schema = RefOr::T(Schema::Array(
2668 ArrayBuilder::new()
2669 .items(RefOr::T(Schema::Object(
2670 ObjectBuilder::new()
2671 .property("element", RefOr::Ref(Ref::new("#/test")))
2672 .build(),
2673 )))
2674 .build(),
2675 ));
2676
2677 let json_str = serde_json::to_string(&ref_or_schema).expect("");
2678 println!("----------------------------");
2679 println!("{json_str}");
2680
2681 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
2682
2683 let json_de_str = serde_json::to_string(&deserialized).expect("");
2684 println!("----------------------------");
2685 println!("{json_de_str}");
2686
2687 assert_eq!(json_str, json_de_str);
2688 }
2689
2690 #[test]
2691 fn serialize_deserialize_schema_array_builder() {
2692 let ref_or_schema = ArrayBuilder::new()
2693 .items(RefOr::T(Schema::Object(
2694 ObjectBuilder::new()
2695 .property("element", RefOr::Ref(Ref::new("#/test")))
2696 .build(),
2697 )))
2698 .build();
2699
2700 let json_str = serde_json::to_string(&ref_or_schema).expect("");
2701 println!("----------------------------");
2702 println!("{json_str}");
2703
2704 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).expect("");
2705
2706 let json_de_str = serde_json::to_string(&deserialized).expect("");
2707 println!("----------------------------");
2708 println!("{json_de_str}");
2709
2710 assert_eq!(json_str, json_de_str);
2711 }
2712
2713 #[test]
2714 fn serialize_deserialize_schema_with_additional_properties() {
2715 let schema = Schema::Object(
2716 ObjectBuilder::new()
2717 .property(
2718 "map",
2719 ObjectBuilder::new()
2720 .additional_properties(Some(AdditionalProperties::FreeForm(true))),
2721 )
2722 .build(),
2723 );
2724
2725 let json_str = serde_json::to_string(&schema).unwrap();
2726 println!("----------------------------");
2727 println!("{json_str}");
2728
2729 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
2730
2731 let json_de_str = serde_json::to_string(&deserialized).unwrap();
2732 println!("----------------------------");
2733 println!("{json_de_str}");
2734
2735 assert_eq!(json_str, json_de_str);
2736 }
2737
2738 #[test]
2739 fn serialize_deserialize_schema_with_additional_properties_object() {
2740 let schema = Schema::Object(
2741 ObjectBuilder::new()
2742 .property(
2743 "map",
2744 ObjectBuilder::new().additional_properties(Some(
2745 ObjectBuilder::new().property("name", Object::with_type(Type::String)),
2746 )),
2747 )
2748 .build(),
2749 );
2750
2751 let json_str = serde_json::to_string(&schema).unwrap();
2752 println!("----------------------------");
2753 println!("{json_str}");
2754
2755 let deserialized: RefOr<Schema> = serde_json::from_str(&json_str).unwrap();
2756
2757 let json_de_str = serde_json::to_string(&deserialized).unwrap();
2758 println!("----------------------------");
2759 println!("{json_de_str}");
2760
2761 assert_eq!(json_str, json_de_str);
2762 }
2763
2764 #[test]
2765 fn serialize_discriminator_with_mapping() {
2766 let mut discriminator = Discriminator::new("type");
2767 discriminator.mapping = [("int".to_string(), "#/components/schemas/MyInt".to_string())]
2768 .into_iter()
2769 .collect::<BTreeMap<_, _>>();
2770 let one_of = OneOfBuilder::new()
2771 .item(Ref::from_schema_name("MyInt"))
2772 .discriminator(Some(discriminator))
2773 .build();
2774 assert_json_snapshot!(one_of, @r##"
2775 {
2776 "oneOf": [
2777 {
2778 "$ref": "#/components/schemas/MyInt"
2779 }
2780 ],
2781 "discriminator": {
2782 "propertyName": "type",
2783 "mapping": {
2784 "int": "#/components/schemas/MyInt"
2785 }
2786 }
2787 }
2788 "##);
2789 }
2790
2791 #[test]
2792 fn serialize_deserialize_object_with_multiple_schema_types() {
2793 let object = ObjectBuilder::new()
2794 .schema_type(SchemaType::from_iter([Type::Object, Type::Null]))
2795 .build();
2796
2797 let json_str = serde_json::to_string(&object).unwrap();
2798 println!("----------------------------");
2799 println!("{json_str}");
2800
2801 let deserialized: Object = serde_json::from_str(&json_str).unwrap();
2802
2803 let json_de_str = serde_json::to_string(&deserialized).unwrap();
2804 println!("----------------------------");
2805 println!("{json_de_str}");
2806
2807 assert_eq!(json_str, json_de_str);
2808 }
2809
2810 #[test]
2811 fn object_with_extensions() {
2812 let expected = json!("value");
2813 let extensions = extensions::ExtensionsBuilder::new()
2814 .add("x-some-extension", expected.clone())
2815 .build();
2816 let json_value = ObjectBuilder::new().extensions(Some(extensions)).build();
2817
2818 let value = serde_json::to_value(&json_value).unwrap();
2819 assert_eq!(value.get("x-some-extension"), Some(&expected));
2820 }
2821
2822 #[test]
2823 fn array_with_extensions() {
2824 let expected = json!("value");
2825 let extensions = extensions::ExtensionsBuilder::new()
2826 .add("x-some-extension", expected.clone())
2827 .build();
2828 let json_value = ArrayBuilder::new().extensions(Some(extensions)).build();
2829
2830 let value = serde_json::to_value(&json_value).unwrap();
2831 assert_eq!(value.get("x-some-extension"), Some(&expected));
2832 }
2833
2834 #[test]
2835 fn oneof_with_extensions() {
2836 let expected = json!("value");
2837 let extensions = extensions::ExtensionsBuilder::new()
2838 .add("x-some-extension", expected.clone())
2839 .build();
2840 let json_value = OneOfBuilder::new().extensions(Some(extensions)).build();
2841
2842 let value = serde_json::to_value(&json_value).unwrap();
2843 assert_eq!(value.get("x-some-extension"), Some(&expected));
2844 }
2845
2846 #[test]
2847 fn allof_with_extensions() {
2848 let expected = json!("value");
2849 let extensions = extensions::ExtensionsBuilder::new()
2850 .add("x-some-extension", expected.clone())
2851 .build();
2852 let json_value = AllOfBuilder::new().extensions(Some(extensions)).build();
2853
2854 let value = serde_json::to_value(&json_value).unwrap();
2855 assert_eq!(value.get("x-some-extension"), Some(&expected));
2856 }
2857
2858 #[test]
2859 fn anyof_with_extensions() {
2860 let expected = json!("value");
2861 let extensions = extensions::ExtensionsBuilder::new()
2862 .add("x-some-extension", expected.clone())
2863 .build();
2864 let json_value = AnyOfBuilder::new().extensions(Some(extensions)).build();
2865
2866 let value = serde_json::to_value(&json_value).unwrap();
2867 assert_eq!(value.get("x-some-extension"), Some(&expected));
2868 }
2869}