1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct Schema {
10 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
11 pub schema_type: Option<SchemaType>,
12
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub format: Option<String>,
15
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub description: Option<String>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub properties: Option<HashMap<String, Schema>>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub required: Option<Vec<String>>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub items: Option<Box<Schema>>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub min_length: Option<usize>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub max_length: Option<usize>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub pattern: Option<String>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub minimum: Option<f64>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub maximum: Option<f64>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub multiple_of: Option<f64>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub min_items: Option<usize>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub max_items: Option<usize>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub unique_items: Option<bool>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub r#enum: Option<Vec<serde_json::Value>>,
61
62 #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
64 pub reference: Option<String>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub any_of: Option<Vec<Schema>>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub all_of: Option<Vec<Schema>>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub one_of: Option<Vec<Schema>>,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub default: Option<serde_json::Value>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub example: Option<serde_json::Value>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub examples: Option<Vec<serde_json::Value>>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub read_only: Option<bool>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub write_only: Option<bool>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub deprecated: Option<bool>,
95
96 #[serde(flatten, skip_serializing_if = "Option::is_none")]
98 pub extensions: Option<HashMap<String, serde_json::Value>>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(rename_all = "lowercase")]
104pub enum SchemaType {
105 String,
106 Number,
107 Integer,
108 Boolean,
109 Array,
110 Object,
111}
112
113impl Schema {
114 pub fn new() -> Self {
116 Self {
117 schema_type: None,
118 format: None,
119 description: None,
120 properties: None,
121 required: None,
122 items: None,
123 min_length: None,
124 max_length: None,
125 pattern: None,
126 minimum: None,
127 maximum: None,
128 multiple_of: None,
129 min_items: None,
130 max_items: None,
131 unique_items: None,
132 r#enum: None,
133 reference: None,
134 any_of: None,
135 all_of: None,
136 one_of: None,
137 default: None,
138 example: None,
139 examples: None,
140 read_only: None,
141 write_only: None,
142 deprecated: None,
143 extensions: None,
144 }
145 }
146
147 pub fn string() -> Self {
149 Self {
150 schema_type: Some(SchemaType::String),
151 ..Self::new()
152 }
153 }
154
155 pub fn integer() -> Self {
157 Self {
158 schema_type: Some(SchemaType::Integer),
159 ..Self::new()
160 }
161 }
162
163 pub fn number() -> Self {
165 Self {
166 schema_type: Some(SchemaType::Number),
167 ..Self::new()
168 }
169 }
170
171 pub fn boolean() -> Self {
173 Self {
174 schema_type: Some(SchemaType::Boolean),
175 ..Self::new()
176 }
177 }
178
179 pub fn array(items: Schema) -> Self {
181 Self {
182 schema_type: Some(SchemaType::Array),
183 items: Some(Box::new(items)),
184 ..Self::new()
185 }
186 }
187
188 pub fn object() -> Self {
190 Self {
191 schema_type: Some(SchemaType::Object),
192 properties: Some(HashMap::new()),
193 ..Self::new()
194 }
195 }
196
197 pub fn reference(name: &str) -> Self {
199 Self {
200 reference: Some(format!("#/components/schemas/{}", name)),
201 ..Self::new()
202 }
203 }
204
205 pub fn format(mut self, format: impl Into<String>) -> Self {
207 self.format = Some(format.into());
208 self
209 }
210
211 pub fn description(mut self, desc: impl Into<String>) -> Self {
213 self.description = Some(desc.into());
214 self
215 }
216
217 pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
219 self.properties
220 .get_or_insert_with(HashMap::new)
221 .insert(name.into(), schema);
222 self
223 }
224
225 pub fn required(mut self, fields: &[&str]) -> Self {
227 self.required = Some(fields.iter().map(|s| s.to_string()).collect());
228 self
229 }
230
231 pub fn min_length(mut self, min: usize) -> Self {
233 self.min_length = Some(min);
234 self
235 }
236
237 pub fn max_length(mut self, max: usize) -> Self {
239 self.max_length = Some(max);
240 self
241 }
242
243 pub fn pattern(mut self, pattern: impl Into<String>) -> Self {
245 self.pattern = Some(pattern.into());
246 self
247 }
248
249 pub fn minimum(mut self, min: impl Into<f64>) -> Self {
251 self.minimum = Some(min.into());
252 self
253 }
254
255 pub fn maximum(mut self, max: impl Into<f64>) -> Self {
257 self.maximum = Some(max.into());
258 self
259 }
260
261 pub fn multiple_of(mut self, divisor: impl Into<f64>) -> Self {
263 self.multiple_of = Some(divisor.into());
264 self
265 }
266
267 pub fn min_items(mut self, min: usize) -> Self {
269 self.min_items = Some(min);
270 self
271 }
272
273 pub fn max_items(mut self, max: usize) -> Self {
275 self.max_items = Some(max);
276 self
277 }
278
279 pub fn unique_items(mut self, unique: bool) -> Self {
281 self.unique_items = Some(unique);
282 self
283 }
284
285 pub fn enum_values<T: Serialize>(mut self, values: &[T]) -> Self {
291 self.r#enum = Some(
292 values
293 .iter()
294 .map(|v| serde_json::to_value(v).expect("Failed to serialize enum value"))
295 .collect(),
296 );
297 self
298 }
299
300 pub fn try_enum_values<T: Serialize>(
304 mut self,
305 values: &[T],
306 ) -> Result<Self, serde_json::Error> {
307 let serialized: Result<Vec<_>, _> = values.iter().map(serde_json::to_value).collect();
308 self.r#enum = Some(serialized?);
309 Ok(self)
310 }
311
312 pub fn any_of(schemas: Vec<Schema>) -> Self {
326 Self {
327 any_of: Some(schemas),
328 ..Self::new()
329 }
330 }
331
332 pub fn all_of(schemas: Vec<Schema>) -> Self {
344 Self {
345 all_of: Some(schemas),
346 ..Self::new()
347 }
348 }
349
350 pub fn one_of(schemas: Vec<Schema>) -> Self {
362 Self {
363 one_of: Some(schemas),
364 ..Self::new()
365 }
366 }
367
368 pub fn default<T: Serialize>(mut self, value: T) -> Self {
382 self.default =
383 Some(serde_json::to_value(value).expect("Failed to serialize default value"));
384 self
385 }
386
387 pub fn try_default<T: Serialize>(mut self, value: T) -> Result<Self, serde_json::Error> {
391 self.default = Some(serde_json::to_value(value)?);
392 Ok(self)
393 }
394
395 pub fn example<T: Serialize>(mut self, value: T) -> Self {
409 self.example =
410 Some(serde_json::to_value(value).expect("Failed to serialize example value"));
411 self
412 }
413
414 pub fn try_example<T: Serialize>(mut self, value: T) -> Result<Self, serde_json::Error> {
418 self.example = Some(serde_json::to_value(value)?);
419 Ok(self)
420 }
421
422 pub fn examples<T: Serialize>(mut self, values: Vec<T>) -> Self {
439 self.examples = Some(
440 values
441 .into_iter()
442 .map(|v| serde_json::to_value(v).expect("Failed to serialize example value"))
443 .collect(),
444 );
445 self
446 }
447
448 pub fn try_examples<T: Serialize>(mut self, values: Vec<T>) -> Result<Self, serde_json::Error> {
452 let serialized: Result<Vec<_>, _> = values
453 .into_iter()
454 .map(|v| serde_json::to_value(v))
455 .collect();
456 self.examples = Some(serialized?);
457 Ok(self)
458 }
459
460 pub fn read_only(mut self, read_only: bool) -> Self {
469 self.read_only = Some(read_only);
470 self
471 }
472
473 pub fn write_only(mut self, write_only: bool) -> Self {
484 self.write_only = Some(write_only);
485 self
486 }
487
488 pub fn deprecated(mut self, deprecated: bool) -> Self {
499 self.deprecated = Some(deprecated);
500 self
501 }
502
503 pub fn extension<T: Serialize>(mut self, key: impl Into<String>, value: T) -> Self {
523 self.extensions.get_or_insert_with(HashMap::new).insert(
524 key.into(),
525 serde_json::to_value(value).expect("Failed to serialize extension value"),
526 );
527 self
528 }
529
530 pub fn try_extension<T: Serialize>(
535 mut self,
536 key: impl Into<String>,
537 value: T,
538 ) -> Result<Self, serde_json::Error> {
539 self.extensions
540 .get_or_insert_with(HashMap::new)
541 .insert(key.into(), serde_json::to_value(value)?);
542 Ok(self)
543 }
544}
545
546impl Default for Schema {
547 fn default() -> Self {
548 Self::new()
549 }
550}
551
552#[cfg(test)]
553mod tests {
554 use super::*;
555
556 #[test]
557 fn test_string_schema() {
558 let schema = Schema::string()
559 .min_length(5)
560 .max_length(100)
561 .format("email");
562
563 assert!(matches!(schema.schema_type, Some(SchemaType::String)));
564 assert_eq!(schema.min_length, Some(5));
565 assert_eq!(schema.max_length, Some(100));
566 assert_eq!(schema.format, Some("email".to_string()));
567 }
568
569 #[test]
570 fn test_integer_schema() {
571 let schema = Schema::integer().minimum(0).maximum(100);
572
573 assert!(matches!(schema.schema_type, Some(SchemaType::Integer)));
574 assert_eq!(schema.minimum, Some(0.0));
575 assert_eq!(schema.maximum, Some(100.0));
576 }
577
578 #[test]
579 fn test_object_schema() {
580 let schema = Schema::object()
581 .property("name", Schema::string())
582 .property("age", Schema::integer().minimum(0))
583 .required(&["name", "age"]);
584
585 assert!(matches!(schema.schema_type, Some(SchemaType::Object)));
586 assert_eq!(schema.properties.as_ref().unwrap().len(), 2);
587 assert_eq!(schema.required.as_ref().unwrap().len(), 2);
588 }
589
590 #[test]
591 fn test_array_schema() {
592 let schema = Schema::array(Schema::string())
593 .min_items(1)
594 .max_items(10)
595 .unique_items(true);
596
597 assert!(matches!(schema.schema_type, Some(SchemaType::Array)));
598 assert_eq!(schema.min_items, Some(1));
599 assert_eq!(schema.max_items, Some(10));
600 assert_eq!(schema.unique_items, Some(true));
601 }
602
603 #[test]
604 fn test_schema_serialization() {
605 let schema = Schema::object()
606 .property("email", Schema::string().format("email"))
607 .property("age", Schema::integer().minimum(18).maximum(120))
608 .required(&["email", "age"]);
609
610 let json = serde_json::to_string_pretty(&schema).unwrap();
611 assert!(json.contains("\"type\": \"object\""));
612 assert!(json.contains("\"email\""));
613 assert!(json.contains("\"age\""));
614 }
615
616 #[test]
619 fn test_any_of_composition() {
620 let schema = Schema::any_of(vec![Schema::string(), Schema::integer()]);
621
622 assert!(schema.any_of.is_some());
623 assert_eq!(schema.any_of.as_ref().unwrap().len(), 2);
624 }
625
626 #[test]
627 fn test_all_of_composition() {
628 let schema = Schema::all_of(vec![
629 Schema::reference("BaseUser"),
630 Schema::object().property("admin", Schema::boolean()),
631 ]);
632
633 assert!(schema.all_of.is_some());
634 assert_eq!(schema.all_of.as_ref().unwrap().len(), 2);
635 }
636
637 #[test]
638 fn test_one_of_composition() {
639 let schema = Schema::one_of(vec![
640 Schema::object().property("type", Schema::string()),
641 Schema::object().property("kind", Schema::string()),
642 ]);
643
644 assert!(schema.one_of.is_some());
645 assert_eq!(schema.one_of.as_ref().unwrap().len(), 2);
646 }
647
648 #[test]
649 fn test_default_value() {
650 use serde_json::json;
651
652 let schema = Schema::string().default(json!("guest"));
653
654 assert!(schema.default.is_some());
655 assert_eq!(schema.default.unwrap(), json!("guest"));
656 }
657
658 #[test]
659 fn test_example() {
660 use serde_json::json;
661
662 let schema = Schema::string().example(json!("john_doe"));
663
664 assert!(schema.example.is_some());
665 assert_eq!(schema.example.unwrap(), json!("john_doe"));
666 }
667
668 #[test]
669 fn test_examples() {
670 use serde_json::json;
671
672 let schema = Schema::string().examples(vec![json!("alice"), json!("bob")]);
673
674 assert!(schema.examples.is_some());
675 assert_eq!(schema.examples.as_ref().unwrap().len(), 2);
676 }
677
678 #[test]
679 fn test_read_only() {
680 let schema = Schema::string().read_only(true);
681
682 assert_eq!(schema.read_only, Some(true));
683 }
684
685 #[test]
686 fn test_write_only() {
687 let schema = Schema::string().format("password").write_only(true);
688
689 assert_eq!(schema.write_only, Some(true));
690 assert_eq!(schema.format, Some("password".to_string()));
691 }
692
693 #[test]
694 fn test_deprecated() {
695 let schema = Schema::string().deprecated(true);
696
697 assert_eq!(schema.deprecated, Some(true));
698 }
699
700 #[test]
701 fn test_vendor_extension() {
702 use serde_json::json;
703
704 let schema = Schema::object().extension(
705 "x-domainstack-validations",
706 json!({"cross_field": ["end > start"]}),
707 );
708
709 assert!(schema.extensions.is_some());
710 let extensions = schema.extensions.as_ref().unwrap();
711 assert!(extensions.contains_key("x-domainstack-validations"));
712 }
713
714 #[test]
715 fn test_composition_serialization() {
716 let schema = Schema::any_of(vec![Schema::string(), Schema::integer()]);
717
718 let json_value = serde_json::to_value(&schema).unwrap();
719 assert!(json_value.get("anyOf").is_some());
720 }
721
722 #[test]
723 fn test_read_write_only_request_response() {
724 let password = Schema::string()
726 .format("password")
727 .write_only(true)
728 .min_length(8);
729
730 let id = Schema::string().read_only(true);
732
733 let user_schema = Schema::object()
734 .property("id", id)
735 .property("email", Schema::string().format("email"))
736 .property("password", password)
737 .required(&["email", "password"]);
738
739 let json = serde_json::to_string_pretty(&user_schema).unwrap();
740 assert!(json.contains("\"writeOnly\": true"));
741 assert!(json.contains("\"readOnly\": true"));
742 }
743
744 #[test]
745 fn test_string_constraints() {
746 let schema = Schema::string()
747 .min_length(5)
748 .max_length(100)
749 .pattern("^[a-z]+$");
750
751 assert_eq!(schema.min_length, Some(5));
752 assert_eq!(schema.max_length, Some(100));
753 assert_eq!(schema.pattern, Some("^[a-z]+$".to_string()));
754 }
755
756 #[test]
757 fn test_numeric_constraints() {
758 let schema = Schema::number()
759 .minimum(0.0)
760 .maximum(100.5)
761 .multiple_of(0.5);
762
763 assert_eq!(schema.minimum, Some(0.0));
764 assert_eq!(schema.maximum, Some(100.5));
765 assert_eq!(schema.multiple_of, Some(0.5));
766 }
767
768 #[test]
769 fn test_array_constraints() {
770 let schema = Schema::array(Schema::string())
771 .min_items(1)
772 .max_items(10)
773 .unique_items(true);
774
775 assert_eq!(schema.min_items, Some(1));
776 assert_eq!(schema.max_items, Some(10));
777 assert_eq!(schema.unique_items, Some(true));
778 }
779
780 #[test]
781 fn test_enum_values() {
782 let schema = Schema::string().enum_values(&["red", "green", "blue"]);
783
784 assert!(schema.r#enum.is_some());
785 let enum_vals = schema.r#enum.unwrap();
786 assert_eq!(enum_vals.len(), 3);
787 }
788
789 #[test]
790 fn test_format_and_description() {
791 let schema = Schema::string()
792 .format("email")
793 .description("User's email address");
794
795 assert_eq!(schema.format, Some("email".to_string()));
796 assert_eq!(schema.description, Some("User's email address".to_string()));
797 }
798
799 #[test]
800 fn test_reference_schema() {
801 let schema = Schema::reference("User");
802
803 assert_eq!(
804 schema.reference,
805 Some("#/components/schemas/User".to_string())
806 );
807 assert!(schema.schema_type.is_none());
808 }
809
810 #[test]
811 fn test_new_schema() {
812 let schema = Schema::new();
813
814 assert!(schema.schema_type.is_none());
815 assert!(schema.properties.is_none());
816 assert!(schema.required.is_none());
817 }
818
819 #[test]
820 fn test_boolean_schema() {
821 let schema = Schema::boolean();
822
823 assert!(matches!(schema.schema_type, Some(SchemaType::Boolean)));
824 }
825
826 #[test]
827 fn test_number_schema() {
828 let schema = Schema::number();
829
830 assert!(matches!(schema.schema_type, Some(SchemaType::Number)));
831 }
832
833 #[test]
835 fn test_try_enum_values_valid() {
836 let schema = Schema::string().try_enum_values(&["red", "green", "blue"]);
837
838 assert!(schema.is_ok());
839 let schema = schema.unwrap();
840 assert!(schema.r#enum.is_some());
841 assert_eq!(schema.r#enum.as_ref().unwrap().len(), 3);
842 }
843
844 #[test]
845 fn test_try_enum_values_with_numbers() {
846 let schema = Schema::integer().try_enum_values(&[1, 2, 3, 5, 8]);
847
848 assert!(schema.is_ok());
849 let schema = schema.unwrap();
850 assert_eq!(schema.r#enum.as_ref().unwrap().len(), 5);
851 }
852
853 #[test]
854 fn test_try_enum_values_empty() {
855 let schema = Schema::string().try_enum_values::<&str>(&[]);
856
857 assert!(schema.is_ok());
858 let schema = schema.unwrap();
859 assert!(schema.r#enum.is_some());
860 assert!(schema.r#enum.as_ref().unwrap().is_empty());
861 }
862
863 #[test]
864 fn test_try_default_valid() {
865 let schema = Schema::string().try_default("guest");
866
867 assert!(schema.is_ok());
868 let schema = schema.unwrap();
869 assert_eq!(schema.default, Some(serde_json::json!("guest")));
870 }
871
872 #[test]
873 fn test_try_default_with_number() {
874 let schema = Schema::integer().try_default(42);
875
876 assert!(schema.is_ok());
877 let schema = schema.unwrap();
878 assert_eq!(schema.default, Some(serde_json::json!(42)));
879 }
880
881 #[test]
882 fn test_try_default_with_object() {
883 use serde_json::json;
884
885 let schema = Schema::object().try_default(json!({"name": "default"}));
886
887 assert!(schema.is_ok());
888 let schema = schema.unwrap();
889 assert_eq!(schema.default, Some(json!({"name": "default"})));
890 }
891
892 #[test]
893 fn test_try_example_valid() {
894 let schema = Schema::string().try_example("john_doe");
895
896 assert!(schema.is_ok());
897 let schema = schema.unwrap();
898 assert_eq!(schema.example, Some(serde_json::json!("john_doe")));
899 }
900
901 #[test]
902 fn test_try_example_with_complex_value() {
903 use serde_json::json;
904
905 let schema = Schema::object().try_example(json!({
906 "name": "Alice",
907 "age": 30,
908 "active": true
909 }));
910
911 assert!(schema.is_ok());
912 }
913
914 #[test]
915 fn test_try_examples_valid() {
916 use serde_json::json;
917
918 let schema = Schema::string().try_examples(vec![json!("alice"), json!("bob")]);
919
920 assert!(schema.is_ok());
921 let schema = schema.unwrap();
922 assert_eq!(schema.examples.as_ref().unwrap().len(), 2);
923 }
924
925 #[test]
926 fn test_try_examples_empty() {
927 let schema = Schema::string().try_examples::<serde_json::Value>(vec![]);
928
929 assert!(schema.is_ok());
930 let schema = schema.unwrap();
931 assert!(schema.examples.as_ref().unwrap().is_empty());
932 }
933
934 #[test]
935 fn test_try_examples_many() {
936 let examples: Vec<_> = (0..100).collect();
937 let schema = Schema::integer().try_examples(examples);
938
939 assert!(schema.is_ok());
940 let schema = schema.unwrap();
941 assert_eq!(schema.examples.as_ref().unwrap().len(), 100);
942 }
943
944 #[test]
945 fn test_try_extension_valid() {
946 use serde_json::json;
947
948 let schema = Schema::object().try_extension("x-custom", json!({"rule": "end > start"}));
949
950 assert!(schema.is_ok());
951 let schema = schema.unwrap();
952 assert!(schema.extensions.as_ref().unwrap().contains_key("x-custom"));
953 }
954
955 #[test]
956 fn test_try_extension_multiple() {
957 use serde_json::json;
958
959 let schema = Schema::object()
960 .try_extension("x-first", json!("value1"))
961 .and_then(|s| s.try_extension("x-second", json!("value2")));
962
963 assert!(schema.is_ok());
964 let schema = schema.unwrap();
965 let exts = schema.extensions.as_ref().unwrap();
966 assert_eq!(exts.len(), 2);
967 }
968
969 #[test]
971 fn test_any_of_empty() {
972 let schema = Schema::any_of(vec![]);
973
974 assert!(schema.any_of.is_some());
975 assert!(schema.any_of.as_ref().unwrap().is_empty());
976 }
977
978 #[test]
979 fn test_all_of_empty() {
980 let schema = Schema::all_of(vec![]);
981
982 assert!(schema.all_of.is_some());
983 assert!(schema.all_of.as_ref().unwrap().is_empty());
984 }
985
986 #[test]
987 fn test_one_of_empty() {
988 let schema = Schema::one_of(vec![]);
989
990 assert!(schema.one_of.is_some());
991 assert!(schema.one_of.as_ref().unwrap().is_empty());
992 }
993
994 #[test]
995 fn test_nested_composition() {
996 let schema = Schema::any_of(vec![
997 Schema::all_of(vec![Schema::string(), Schema::integer()]),
998 Schema::one_of(vec![Schema::boolean(), Schema::number()]),
999 ]);
1000
1001 assert!(schema.any_of.is_some());
1002 let any_of = schema.any_of.as_ref().unwrap();
1003 assert!(any_of[0].all_of.is_some());
1004 assert!(any_of[1].one_of.is_some());
1005 }
1006
1007 #[test]
1009 fn test_multiple_format_calls() {
1010 let schema = Schema::string().format("email").format("hostname");
1011
1012 assert_eq!(schema.format, Some("hostname".to_string()));
1014 }
1015
1016 #[test]
1017 fn test_multiple_description_calls() {
1018 let schema = Schema::string()
1019 .description("First description")
1020 .description("Second description");
1021
1022 assert_eq!(schema.description, Some("Second description".to_string()));
1023 }
1024
1025 #[test]
1026 fn test_multiple_min_max_calls() {
1027 let schema = Schema::integer()
1028 .minimum(0)
1029 .maximum(100)
1030 .minimum(10)
1031 .maximum(50);
1032
1033 assert_eq!(schema.minimum, Some(10.0));
1035 assert_eq!(schema.maximum, Some(50.0));
1036 }
1037
1038 #[test]
1040 fn test_reference_with_path() {
1041 let schema = Schema::reference("nested/Type");
1042
1043 assert_eq!(
1044 schema.reference,
1045 Some("#/components/schemas/nested/Type".to_string())
1046 );
1047 }
1048
1049 #[test]
1050 fn test_reference_empty_name() {
1051 let schema = Schema::reference("");
1052
1053 assert_eq!(schema.reference, Some("#/components/schemas/".to_string()));
1054 }
1055
1056 #[test]
1058 fn test_deep_object_nesting() {
1059 let schema = Schema::object()
1060 .property(
1061 "level1",
1062 Schema::object().property(
1063 "level2",
1064 Schema::object().property("level3", Schema::string()),
1065 ),
1066 )
1067 .required(&["level1"]);
1068
1069 let props = schema.properties.as_ref().unwrap();
1070 assert!(props.contains_key("level1"));
1071 }
1072
1073 #[test]
1074 fn test_array_of_objects() {
1075 let item_schema = Schema::object()
1076 .property("id", Schema::integer())
1077 .property("name", Schema::string())
1078 .required(&["id", "name"]);
1079
1080 let schema = Schema::array(item_schema).min_items(0).max_items(100);
1081
1082 assert!(schema.items.is_some());
1083 assert_eq!(schema.min_items, Some(0));
1084 }
1085
1086 #[test]
1088 fn test_schema_default_trait() {
1089 let schema: Schema = Default::default();
1090
1091 assert!(schema.schema_type.is_none());
1092 assert!(schema.properties.is_none());
1093 }
1094
1095 #[test]
1097 fn test_full_schema_serialization() {
1098 use serde_json::json;
1099
1100 let schema = Schema::object()
1101 .property("id", Schema::string().read_only(true))
1102 .property("name", Schema::string().min_length(1).max_length(100))
1103 .property("score", Schema::number().minimum(0.0).maximum(100.0))
1104 .property("tags", Schema::array(Schema::string()).unique_items(true))
1105 .required(&["name"])
1106 .description("A user object")
1107 .deprecated(false)
1108 .example(json!({"name": "Alice", "score": 95.5}));
1109
1110 let json_str = serde_json::to_string(&schema).unwrap();
1111 assert!(json_str.contains("\"description\""));
1112 assert!(json_str.contains("\"readOnly\""));
1113 }
1114}