1use std::collections::BTreeSet;
4
5use crate::merge::{merge_all, try_merge_with_subschemas};
6use crate::type_entry::{
7 EnumTagType, TypeEntry, TypeEntryDetails, TypeEntryEnum, TypeEntryNewtype, TypeEntryStruct,
8 Variant, VariantDetails,
9};
10use crate::util::{all_mutually_exclusive, ref_key, StringValidator};
11use log::{debug, info};
12use schemars::schema::{
13 ArrayValidation, InstanceType, Metadata, ObjectValidation, Schema, SchemaObject, SingleOrVec,
14 StringValidation, SubschemaValidation,
15};
16
17use crate::util::get_type_name;
18
19use crate::{Error, Name, Result, TypeSpace, TypeSpaceImpl};
20
21pub const STD_NUM_NONZERO_PREFIX: &str = "::std::num::NonZero";
22
23impl TypeSpace {
24 pub(crate) fn convert_schema<'a>(
25 &mut self,
26 type_name: Name,
27 schema: &'a Schema,
28 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
29 info!(
30 "convert_schema {:?} {}",
31 type_name,
32 serde_json::to_string_pretty(schema).unwrap()
33 );
34 match schema {
35 Schema::Object(obj) => {
36 if let Some(type_entry) = self.cache.lookup(obj) {
37 Ok((type_entry, &obj.metadata))
38 } else {
39 self.convert_schema_object(type_name, schema, obj)
40 }
41 }
42
43 Schema::Bool(true) => self.convert_permissive(&None),
44 Schema::Bool(false) => self.convert_never(type_name, schema),
45 }
46 }
47
48 pub(crate) fn convert_schema_object<'a>(
49 &mut self,
50 type_name: Name,
51 original_schema: &'a Schema,
52 schema: &'a SchemaObject,
53 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
54 if let Some(type_entry) = self.convert_rust_extension(schema) {
55 return Ok((type_entry, &schema.metadata));
56 }
57
58 match schema {
59 SchemaObject {
64 metadata,
65 instance_type: Some(SingleOrVec::Vec(multiple)),
66 enum_values,
67 ..
68 } if multiple.len() == 2 && multiple.contains(&InstanceType::Null) => {
69 let only_null = enum_values
70 .as_ref()
71 .is_some_and(|values| values.iter().all(serde_json::Value::is_null));
72
73 if only_null {
74 self.convert_null(metadata)
77 } else if let Some(other_type) = multiple.iter().find(|t| t != &&InstanceType::Null)
78 {
79 let enum_values = enum_values.clone().map(|values| {
82 values
83 .iter()
84 .filter(|&value| !value.is_null())
85 .cloned()
86 .collect()
87 });
88 let ss = Schema::Object(SchemaObject {
89 instance_type: Some(SingleOrVec::from(*other_type)),
90 enum_values,
91 ..schema.clone()
92 });
93 let inner_type_name = match &type_name {
99 Name::Required(name) => Name::Suggested(format!("{}Inner", name)),
100 _ => type_name,
101 };
102 self.convert_option(inner_type_name, metadata, &ss)
103 } else {
104 let new_schema = SchemaObject {
106 instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Null))),
107 ..schema.clone()
108 };
109 self.convert_schema_object(type_name, original_schema, &new_schema)
110 .map(|(te, m)| match m {
111 Some(_) if m == metadata => (te, metadata),
112 Some(_) => panic!("unexpected metadata value"),
113 None => (te, &None),
114 })
115 }
116 }
117
118 SchemaObject {
120 metadata,
121 instance_type: Some(SingleOrVec::Single(single)),
122 format,
123 enum_values: None,
124 const_value: None,
125 subschemas: None,
126 number: _,
127 string,
128 array: _,
129 object: _,
130 reference: None,
131 extensions: _,
132 } if single.as_ref() == &InstanceType::String => self.convert_string(
133 type_name,
134 original_schema,
135 metadata,
136 format,
137 string.as_ref().map(Box::as_ref),
138 ),
139
140 SchemaObject {
142 metadata,
143 instance_type: None,
144 format,
145 enum_values: None,
146 const_value: None,
147 subschemas: None,
148 number: None,
149 string: string @ Some(_),
150 array: None,
151 object: None,
152 reference: None,
153 extensions: _,
154 } => self.convert_string(
155 type_name,
156 original_schema,
157 metadata,
158 format,
159 string.as_ref().map(Box::as_ref),
160 ),
161
162 SchemaObject {
164 metadata,
165 instance_type: Some(SingleOrVec::Single(single)),
166 format: _,
172 enum_values: Some(enum_values),
173 const_value: None,
174 subschemas: None,
175 number: _,
176 string,
177 array: _,
178 object: _,
179 reference: None,
180 extensions: _,
181 } if single.as_ref() == &InstanceType::String => self.convert_enum_string(
182 type_name,
183 original_schema,
184 metadata,
185 enum_values,
186 string.as_ref().map(Box::as_ref),
187 ),
188
189 SchemaObject {
191 metadata,
192 instance_type: Some(SingleOrVec::Single(single)),
193 format,
194 enum_values: None,
195 const_value: None,
196 subschemas: None,
197 number: validation,
198 string: _,
199 array: _,
200 object: _,
201 reference: None,
202 extensions: _,
203 } if single.as_ref() == &InstanceType::Integer => {
204 self.convert_integer(metadata, validation, format)
205 }
206
207 SchemaObject {
209 metadata,
210 instance_type: Some(SingleOrVec::Single(single)),
211 format,
212 enum_values: None,
213 const_value: None,
214 subschemas: None,
215 number: validation,
216 string: _,
217 array: _,
218 object: _,
219 reference: None,
220 extensions: _,
221 } if single.as_ref() == &InstanceType::Number => {
222 self.convert_number(metadata, validation, format)
223 }
224
225 SchemaObject {
227 metadata,
228 instance_type: Some(SingleOrVec::Single(single)),
229 format: None,
230 enum_values: _,
231 const_value: None,
232 subschemas: None,
233 number: _,
234 string: _,
235 array: _,
236 object: _,
237 reference: None,
238 extensions: _,
239 } if single.as_ref() == &InstanceType::Boolean => self.convert_bool(metadata),
240
241 SchemaObject {
243 metadata,
244 instance_type: Some(SingleOrVec::Single(single)),
245 format: None,
246 enum_values: None,
247 const_value: None,
248 subschemas: None,
249 number: _,
250 string: _,
251 array: _,
252 object: validation,
253 reference: None,
254 extensions: _,
255 } if single.as_ref() == &InstanceType::Object => {
256 self.convert_object(type_name, original_schema, metadata, validation)
257 }
258
259 SchemaObject {
261 metadata,
262 instance_type: None,
263 format: None,
264 enum_values: None,
265 const_value: None,
266 subschemas: None,
267 number: None,
268 string: None,
269 array: None,
270 object: validation @ Some(_),
271 reference: None,
272 extensions: _,
273 } => self.convert_object(type_name, original_schema, metadata, validation),
274
275 SchemaObject {
277 metadata,
278 instance_type: Some(SingleOrVec::Single(single)),
279 format: None,
280 enum_values: None,
281 const_value: None,
282 subschemas: None,
283 number: _,
284 string: _,
285 array: Some(validation),
286 object: _,
287 reference: None,
288 extensions: _,
289 } if single.as_ref() == &InstanceType::Array => {
290 self.convert_array(type_name, metadata, validation)
291 }
292
293 SchemaObject {
295 metadata,
296 instance_type: None,
297 format: None,
298 enum_values: None,
299 const_value: None,
300 subschemas: None,
301 number: None,
302 string: None,
303 array: Some(validation),
304 object: None,
305 reference: None,
306 extensions: _,
307 } => self.convert_array(type_name, metadata, validation),
308
309 SchemaObject {
311 metadata,
312 instance_type: Some(SingleOrVec::Single(single)),
313 format: None,
314 enum_values: None,
315 const_value: None,
316 subschemas: None,
317 number: _,
318 string: _,
319 array: None,
320 object: _,
321 reference: None,
322 extensions: _,
323 } if single.as_ref() == &InstanceType::Array => self.convert_array_of_any(metadata),
324
325 SchemaObject {
327 metadata,
328 instance_type: None,
329 format: None,
330 enum_values: None,
331 const_value: None,
332 subschemas: None,
333 number: None,
334 string: None,
335 array: None,
336 object: None,
337 reference: None,
338 extensions: _,
339 } => self.convert_permissive(metadata),
340
341 SchemaObject {
343 metadata,
344 instance_type: Some(SingleOrVec::Single(single)),
345 format: _,
346 enum_values: None,
347 const_value: None,
348 subschemas: None,
349 number: _,
350 string: _,
351 array: _,
352 object: _,
353 reference: None,
354 extensions: _,
355 } if single.as_ref() == &InstanceType::Null => self.convert_null(metadata),
356
357 SchemaObject {
359 metadata,
360 instance_type: None,
361 format: None,
362 enum_values: None,
363 const_value: None,
364 subschemas: None,
365 number: None,
366 string: None,
367 array: None,
368 object: None,
369 reference: Some(reference),
370 extensions: _,
371 } => self.convert_reference(metadata, reference),
372
373 SchemaObject {
381 metadata,
382 instance_type,
383 format: None,
384 enum_values: None,
385 const_value: None,
386 subschemas: None,
387 number: None,
388 string: None,
389 array: None,
390 object: None,
391 reference: Some(reference),
392 extensions: _,
393 } => {
394 let ref_schema = self.definitions.get(&ref_key(reference)).unwrap();
395 assert!(matches!(ref_schema, Schema::Object(SchemaObject {
396 instance_type: it, ..
397 }) if instance_type == it));
398
399 self.convert_reference(metadata, reference)
400 }
401
402 SchemaObject {
403 metadata,
404 instance_type: _,
405 format: _,
406 enum_values: _,
407 const_value: _,
408 subschemas: _,
409 number: _,
410 string: _,
411 array: _,
412 object: _,
413 reference: Some(reference),
414 extensions: _,
415 } => {
416 let mut def = self.definitions.get(&ref_key(reference)).unwrap();
417 let mut new_schema = Schema::Object(SchemaObject {
418 reference: None,
419 ..schema.clone()
420 });
421 while let Schema::Object(SchemaObject { reference: r, .. }) = def {
422 let schema_only_ref = Schema::Object(SchemaObject {
423 reference: r.clone(),
424 ..Default::default()
425 });
426 let schema_without_ref = Schema::Object(SchemaObject {
427 reference: None,
428 ..def.clone().into_object()
429 });
430 new_schema = merge_all(
431 &[schema_without_ref, schema_only_ref, new_schema],
432 &self.definitions,
433 );
434 if let Some(r) = r {
435 def = self.definitions.get(&ref_key(r)).unwrap();
436 } else {
437 break;
438 }
439 }
440 let (type_entry, _) = self.convert_schema(type_name, &new_schema).unwrap();
441 Ok((type_entry, metadata))
442 }
443
444 SchemaObject {
446 instance_type: Some(SingleOrVec::Single(_)),
447 enum_values: Some(enum_values),
448 ..
449 } => self.convert_typed_enum(type_name, original_schema, schema, enum_values),
450
451 SchemaObject {
453 metadata,
454 instance_type: None,
455 format: None,
456 enum_values: Some(enum_values),
457 const_value: None,
458 subschemas: None,
459 number: None,
460 string: None,
461 array: None,
462 object: None,
463 reference: None,
464 extensions: _,
465 } => self.convert_unknown_enum(type_name, original_schema, metadata, enum_values),
466
467 SchemaObject {
469 metadata,
470 instance_type: _,
472 format: None,
473 enum_values: None,
474 const_value: None,
475 subschemas: Some(subschemas),
476 number: None,
477 string: None,
478 array: None,
479 object: None,
480 reference: None,
481 extensions: _,
482 } => match subschemas.as_ref() {
483 SubschemaValidation {
484 all_of: Some(subschemas),
485 any_of: None,
486 one_of: None,
487 not: None,
488 if_schema: None,
489 then_schema: None,
490 else_schema: None,
491 } => self.convert_all_of(type_name, original_schema, metadata, subschemas),
492 SubschemaValidation {
493 all_of: None,
494 any_of: Some(subschemas),
495 one_of: None,
496 not: None,
497 if_schema: None,
498 then_schema: None,
499 else_schema: None,
500 } => self.convert_any_of(type_name, original_schema, metadata, subschemas),
501 SubschemaValidation {
502 all_of: None,
503 any_of: None,
504 one_of: Some(subschemas),
505 not: None,
506 if_schema: None,
507 then_schema: None,
508 else_schema: None,
509 } => self.convert_one_of(type_name, original_schema, metadata, subschemas),
510 SubschemaValidation {
511 all_of: None,
512 any_of: None,
513 one_of: None,
514 not: Some(subschema),
515 if_schema: None,
516 then_schema: None,
517 else_schema: None,
518 } => self.convert_not(type_name, original_schema, metadata, subschema),
519
520 subschemas => {
523 let schema_object = SchemaObject {
525 subschemas: None,
526 ..schema.clone()
527 };
528 let merged_schema = try_merge_with_subschemas(
529 schema_object,
530 Some(subschemas),
531 &self.definitions,
532 );
533 match merged_schema {
534 Ok(s) => {
535 let (type_entry, _) =
536 self.convert_schema_object(type_name, original_schema, &s)?;
537 Ok((type_entry, &None))
538 }
539 Err(_) => self.convert_never(type_name, original_schema),
541 }
542 }
543 },
544
545 SchemaObject {
547 metadata,
548 subschemas: subschemas @ Some(_),
549 ..
550 } => {
551 let without_subschemas = SchemaObject {
552 subschemas: None,
553 metadata: None,
554 ..schema.clone()
555 };
556 debug!(
557 "pre merged schema {}",
558 serde_json::to_string_pretty(schema).unwrap(),
559 );
560 match try_merge_with_subschemas(
561 without_subschemas,
562 subschemas.as_deref(),
563 &self.definitions,
564 ) {
565 Ok(merged_schema) => {
566 let merged_schema = SchemaObject {
568 metadata: metadata.clone(),
569 ..merged_schema
570 };
571 debug!(
572 "merged schema {}",
573 serde_json::to_string_pretty(&merged_schema).unwrap(),
574 );
575
576 let (type_entry, _) =
577 self.convert_schema_object(type_name, original_schema, &merged_schema)?;
578 Ok((type_entry, &None))
579 }
580
581 Err(_) => self.convert_never(type_name, original_schema),
582 }
583 }
584
585 SchemaObject {
591 metadata,
592 const_value: Some(_),
593 ..
594 } => {
595 let new_schema = SchemaObject {
596 const_value: None,
597 ..schema.clone()
598 };
599 self.convert_schema_object(type_name, original_schema, &new_schema)
600 .map(|(te, m)| match m {
601 Some(_) if m == metadata => (te, metadata),
602 Some(_) => panic!("unexpected metadata value"),
603 None => (te, &None),
604 })
605 }
606
607 SchemaObject {
612 instance_type: Some(SingleOrVec::Vec(instance_types)),
613 metadata,
614 ..
615 } if instance_types.contains(&InstanceType::Null)
616 && instance_types.contains(&InstanceType::Boolean)
617 && instance_types.contains(&InstanceType::Object)
618 && instance_types.contains(&InstanceType::Array)
619 && instance_types.contains(&InstanceType::Number)
620 && instance_types.contains(&InstanceType::String)
621 && instance_types.contains(&InstanceType::Integer) =>
622 {
623 let (type_entry, _) = self.convert_schema_object(
624 type_name,
625 original_schema,
626 &SchemaObject {
627 instance_type: None,
628 ..schema.clone()
629 },
630 )?;
631 Ok((type_entry, metadata))
632 }
633
634 SchemaObject {
636 metadata,
637 instance_type: Some(SingleOrVec::Vec(instance_types)),
638 ..
639 } if instance_types.len() == 1 => {
640 let [it] = instance_types.as_slice() else {
641 unreachable!()
642 };
643 let (type_entry, _) = self.convert_schema_object(
644 type_name,
645 original_schema,
646 &SchemaObject {
647 instance_type: Some(SingleOrVec::Single((*it).into())),
648 ..schema.clone()
649 },
650 )?;
651 Ok((type_entry, metadata))
652 }
653
654 SchemaObject {
660 metadata,
661 instance_type: Some(SingleOrVec::Vec(instance_types)),
662 format,
663 enum_values: None,
664 const_value: None,
665 subschemas: None,
666 number,
667 string,
668 array,
669 object,
670 reference: None,
671 extensions: _,
672 } => {
673 let unique_types = instance_types.iter().collect::<BTreeSet<_>>();
676
677 let subschemas = unique_types
697 .into_iter()
698 .map(|it| {
699 let instance_type = Some(SingleOrVec::Single(Box::new(*it)));
700 let (label, inner_schema) = match it {
701 InstanceType::Null => (
702 "null",
703 SchemaObject {
704 instance_type,
705 ..Default::default()
706 },
707 ),
708 InstanceType::Boolean => (
709 "boolean",
710 SchemaObject {
711 instance_type,
712 ..Default::default()
713 },
714 ),
715 InstanceType::Object => (
716 "object",
717 SchemaObject {
718 instance_type,
719 object: object.clone(),
720 ..Default::default()
721 },
722 ),
723 InstanceType::Array => (
724 "array",
725 SchemaObject {
726 instance_type,
727 array: array.clone(),
728 ..Default::default()
729 },
730 ),
731 InstanceType::Number => (
732 "number",
733 SchemaObject {
734 instance_type,
735 format: format.clone(),
736 number: number.clone(),
737 ..Default::default()
738 },
739 ),
740 InstanceType::String => (
741 "string",
742 SchemaObject {
743 instance_type,
744 format: format.clone(),
745 string: string.clone(),
746 ..Default::default()
747 },
748 ),
749 InstanceType::Integer => (
750 "integer",
751 SchemaObject {
752 instance_type,
753 format: format.clone(),
754 number: number.clone(),
755 ..Default::default()
756 },
757 ),
758 };
759 Schema::Object(SchemaObject {
761 metadata: Some(Box::new(Metadata {
762 title: Some(label.to_string()),
763 ..Default::default()
764 })),
765 subschemas: Some(Box::new(SubschemaValidation {
766 all_of: Some(vec![inner_schema.into()]),
767 ..Default::default()
768 })),
769 ..Default::default()
770 })
771 })
772 .collect::<Vec<_>>();
773
774 let type_entry =
775 self.untagged_enum(type_name, original_schema, metadata, &subschemas)?;
776 Ok((type_entry, metadata))
777 }
778
779 SchemaObject { .. } => todo!(
781 "invalid (or unexpected) schema:\n{}",
782 serde_json::to_string_pretty(schema).unwrap()
783 ),
784 }
785 }
786
787 fn convert_string<'a>(
788 &mut self,
789 type_name: Name,
790 original_schema: &'a Schema,
791 metadata: &'a Option<Box<Metadata>>,
792 format: &Option<String>,
793 validation: Option<&StringValidation>,
794 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
795 match format.as_ref().map(String::as_str) {
796 None => match validation {
797 None
800 | Some(schemars::schema::StringValidation {
801 max_length: None,
802 min_length: None,
803 pattern: None,
804 }) => Ok((TypeEntryDetails::String.into(), metadata)),
805
806 Some(validation) => {
807 if let Some(pattern) = &validation.pattern {
808 let _ = regress::Regex::new(pattern).map_err(|e| Error::InvalidSchema {
809 type_name: type_name.clone().into_option(),
810 reason: format!("invalid pattern '{}' {}", pattern, e),
811 })?;
812 self.uses_regress = true;
813 }
814
815 let string = TypeEntryDetails::String.into();
816 let type_id = self.assign_type(string);
817 Ok((
818 TypeEntryNewtype::from_metadata_with_string_validation(
819 self,
820 type_name,
821 metadata,
822 type_id,
823 validation,
824 original_schema.clone(),
825 ),
826 metadata,
827 ))
828 }
829 },
830
831 Some("uuid") => {
832 self.uses_uuid = true;
833 Ok((
834 TypeEntry::new_native(
835 "::uuid::Uuid",
836 &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
837 ),
838 metadata,
839 ))
840 }
841
842 Some("date") => {
843 self.uses_chrono = true;
844 Ok((
845 TypeEntry::new_native(
846 "::chrono::naive::NaiveDate",
847 &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
848 ),
849 metadata,
850 ))
851 }
852 Some("date-time") => {
853 self.uses_chrono = true;
854 Ok((
855 TypeEntry::new_native(
856 "::chrono::DateTime<::chrono::offset::Utc>",
857 &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
858 ),
859 metadata,
860 ))
861 }
862
863 Some("ip") => Ok((
864 TypeEntry::new_native(
865 "::std::net::IpAddr",
866 &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
867 ),
868 metadata,
869 )),
870 Some("ipv4") => Ok((
871 TypeEntry::new_native(
872 "::std::net::Ipv4Addr",
873 &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
874 ),
875 metadata,
876 )),
877 Some("ipv6") => Ok((
878 TypeEntry::new_native(
879 "::std::net::Ipv6Addr",
880 &[TypeSpaceImpl::Display, TypeSpaceImpl::FromStr],
881 ),
882 metadata,
883 )),
884
885 Some(unhandled) => {
886 info!("treating a string format '{}' as a String", unhandled);
887 Ok((TypeEntryDetails::String.into(), metadata))
888 }
889 }
890 }
891
892 pub(crate) fn convert_enum_string<'a>(
893 &mut self,
894 type_name: Name,
895 original_schema: &'a Schema,
896 metadata: &'a Option<Box<Metadata>>,
897 enum_values: &[serde_json::Value],
898 validation: Option<&StringValidation>,
899 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
900 let mut has_null = false;
911
912 let validator = StringValidator::new(&type_name, validation)?;
913
914 let variants = enum_values
915 .iter()
916 .flat_map(|value| match value {
917 serde_json::Value::Null => {
920 has_null = true;
921 None
922 }
923 serde_json::Value::String(variant_name) if validator.is_valid(variant_name) => {
924 Some(Ok(Variant::new(
925 variant_name.clone(),
926 None,
927 VariantDetails::Simple,
928 )))
929 }
930
931 serde_json::Value::String(_) => None,
936
937 _ => Some(Err(Error::BadValue("string".to_string(), value.clone()))),
938 })
939 .collect::<Result<Vec<Variant>>>()?;
940
941 if variants.is_empty() {
942 if has_null {
943 self.convert_null(metadata)
944 } else {
945 self.convert_never(type_name, original_schema)
946 }
947 } else {
948 let mut ty = TypeEntryEnum::from_metadata(
949 self,
950 type_name,
951 metadata,
952 EnumTagType::External,
953 variants,
954 false,
955 original_schema.clone(),
956 );
957
958 if has_null {
959 ty = self.type_to_option(ty);
960 }
961
962 Ok((ty, metadata))
963 }
964 }
965
966 fn convert_integer<'a>(
967 &self,
968 metadata: &'a Option<Box<Metadata>>,
969 validation: &Option<Box<schemars::schema::NumberValidation>>,
970 format: &Option<String>,
971 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
972 let (mut min, mut max, multiple) = if let Some(validation) = validation {
973 let min = match (&validation.minimum, &validation.exclusive_minimum) {
974 (None, None) => None,
975 (None, Some(value)) => Some(value + 1.0),
976 (Some(value), None) => Some(*value),
977 (Some(min), Some(emin)) => Some(min.max(emin + 1.0)),
978 };
979 let max = match (&validation.maximum, &validation.exclusive_maximum) {
980 (None, None) => None,
981 (None, Some(value)) => Some(value - 1.0),
982 (Some(value), None) => Some(*value),
983 (Some(max), Some(emax)) => Some(max.min(emax - 1.0)),
984 };
985 (min, max, validation.multiple_of)
986 } else {
987 (None, None, None)
988 };
989
990 let formats: &[(&str, &str, &str, f64, f64)] = &[
993 (
994 "int8",
995 "i8",
996 "::std::num::NonZeroU8",
997 i8::MIN as f64,
998 i8::MAX as f64,
999 ),
1000 (
1001 "uint8",
1002 "u8",
1003 "::std::num::NonZeroU8",
1004 u8::MIN as f64,
1005 u8::MAX as f64,
1006 ),
1007 (
1008 "int16",
1009 "i16",
1010 "::std::num::NonZeroU16",
1011 i16::MIN as f64,
1012 i16::MAX as f64,
1013 ),
1014 (
1015 "uint16",
1016 "u16",
1017 "::std::num::NonZeroU16",
1018 u16::MIN as f64,
1019 u16::MAX as f64,
1020 ),
1021 (
1022 "int",
1023 "i32",
1024 "::std::num::NonZeroU32",
1025 i32::MIN as f64,
1026 i32::MAX as f64,
1027 ),
1028 (
1029 "int32",
1030 "i32",
1031 "::std::num::NonZeroU32",
1032 i32::MIN as f64,
1033 i32::MAX as f64,
1034 ),
1035 (
1036 "uint",
1037 "u32",
1038 "::std::num::NonZeroU32",
1039 u32::MIN as f64,
1040 u32::MAX as f64,
1041 ),
1042 (
1043 "uint32",
1044 "u32",
1045 "::std::num::NonZeroU32",
1046 u32::MIN as f64,
1047 u32::MAX as f64,
1048 ),
1049 (
1052 "int64",
1053 "i64",
1054 "::std::num::NonZeroU64",
1055 i64::MIN as f64,
1056 i64::MAX as f64,
1057 ),
1058 (
1059 "uint64",
1060 "u64",
1061 "::std::num::NonZeroU64",
1062 u64::MIN as f64,
1063 u64::MAX as f64,
1064 ),
1065 ];
1066
1067 if let Some(format) = format {
1068 if let Some((_fmt, ty, nz_ty, imin, imax)) = formats
1069 .iter()
1070 .find(|(int_format, _, _, _, _)| int_format == format)
1071 {
1072 let valid_min = min.is_none() || min.map(|fmin| fmin.ge(imin)).unwrap_or(false);
1073 let valid_max = max.is_none() || max.map(|fmax| fmax.le(imax)).unwrap_or(false);
1074 if multiple.is_none() && valid_min && valid_max {
1075 if let Some(default) = metadata
1079 .as_ref()
1080 .and_then(|m| m.default.as_ref())
1081 .and_then(|v| v.as_f64())
1082 {
1083 if default < *imin || default > *imax {
1084 return Err(Error::InvalidValue);
1085 }
1086 }
1087
1088 if min == Some(1.) {
1090 return Ok((TypeEntry::new_integer(nz_ty), metadata));
1091 } else {
1092 return Ok((TypeEntry::new_integer(ty), metadata));
1093 }
1094 }
1095
1096 if min.is_none() {
1097 min = Some(*imin);
1098 }
1099 if max.is_none() {
1100 max = Some(*imax);
1101 }
1102 }
1103 }
1104
1105 if let Some(default) = metadata.as_ref().and_then(|m| m.default.as_ref()) {
1108 match (default.as_f64(), min, max) {
1113 (Some(_), None, None) => Some(()),
1114 (Some(value), None, Some(fmax)) if value <= fmax => Some(()),
1115 (Some(value), Some(fmin), None) if value >= fmin => Some(()),
1116 (Some(value), Some(fmin), Some(fmax)) if value >= fmin && value <= fmax => Some(()),
1117 _ => None,
1118 }
1119 .ok_or(Error::InvalidValue)?;
1120 }
1121
1122 let maybe_type = match (min, max) {
1124 (None, Some(max)) => formats.iter().rev().find_map(|(_, ty, _nz_ty, _, imax)| {
1125 if (imax - max).abs() <= f64::EPSILON {
1126 Some(ty.to_string())
1127 } else {
1128 None
1129 }
1130 }),
1131 (Some(min), None) => formats.iter().rev().find_map(|(_, ty, nz_ty, imin, _)| {
1132 if min == 1. {
1133 Some(nz_ty.to_string())
1134 } else if (imin - min).abs() <= f64::EPSILON {
1135 Some(ty.to_string())
1136 } else {
1137 None
1138 }
1139 }),
1140 (Some(min), Some(max)) => {
1141 formats.iter().rev().find_map(|(_, ty, nz_ty, imin, imax)| {
1142 if min == 1. {
1143 Some(nz_ty.to_string())
1144 } else if (imax - max).abs() <= f64::EPSILON
1145 && (imin - min).abs() <= f64::EPSILON
1146 {
1147 Some(ty.to_string())
1148 } else {
1149 None
1150 }
1151 })
1152 }
1153 (None, None) => None,
1154 };
1155
1156 if let Some(ty) = maybe_type {
1158 Ok((TypeEntry::new_integer(ty), metadata))
1159 } else {
1160 Ok((TypeEntry::new_integer("i64"), metadata))
1165 }
1166 }
1167
1168 fn convert_number<'a>(
1170 &self,
1171 _metadata: &'a Option<Box<Metadata>>,
1172 _validation: &Option<Box<schemars::schema::NumberValidation>>,
1173 format: &Option<String>,
1174 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1175 match format.as_deref() {
1187 Some("float") => Ok((TypeEntry::new_float("f32"), &None)),
1188 _ => Ok((TypeEntry::new_float("f64"), &None)),
1189 }
1190 }
1191
1192 fn convert_null<'a>(
1195 &self,
1196 metadata: &'a Option<Box<Metadata>>,
1197 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1198 Ok((TypeEntryDetails::Unit.into(), metadata))
1199 }
1200
1201 fn can_handle_pattern_properties(validation: &ObjectValidation) -> bool {
1203 if !validation.required.is_empty() {
1204 return false;
1205 }
1206
1207 if !validation.properties.is_empty() {
1208 return false;
1209 }
1210
1211 let Some(first_schema) = validation.pattern_properties.values().next() else {
1214 return false;
1215 };
1216
1217 if !validation
1218 .pattern_properties
1219 .values()
1220 .all(|schema| schema == first_schema)
1221 {
1222 return false;
1223 }
1224
1225 if validation.additional_properties.as_ref().map(AsRef::as_ref) == Some(&Schema::Bool(true))
1227 || matches!(
1228 validation.additional_properties.as_ref().map(AsRef::as_ref),
1229 Some(&Schema::Object(_))
1230 )
1231 {
1232 return false;
1233 }
1234
1235 if validation.property_names.is_some() {
1238 return false;
1239 }
1240
1241 true
1242 }
1243
1244 fn convert_object<'a>(
1245 &mut self,
1246 type_name: Name,
1247 original_schema: &'a Schema,
1248 metadata: &'a Option<Box<Metadata>>,
1249 validation: &Option<Box<ObjectValidation>>,
1250 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1251 match validation.as_ref().map(Box::as_ref) {
1252 Some(ObjectValidation {
1255 max_properties: _,
1256 min_properties: _,
1257 required,
1258 properties,
1259 pattern_properties,
1260 additional_properties,
1261 property_names,
1262 }) if required.is_empty()
1263 && properties.is_empty()
1264 && pattern_properties.is_empty()
1265 && additional_properties.as_ref().map(AsRef::as_ref)
1266 != Some(&Schema::Bool(false)) =>
1267 {
1268 let type_entry = self.make_map(
1269 type_name.into_option(),
1270 property_names,
1271 additional_properties,
1272 )?;
1273 Ok((type_entry, metadata))
1274 }
1275
1276 Some(validation) if Self::can_handle_pattern_properties(validation) => {
1277 let pattern = validation
1278 .pattern_properties
1279 .keys()
1280 .cloned()
1281 .collect::<Vec<_>>()
1282 .join("|");
1283
1284 let property_names = Some(Box::new(Schema::Object(SchemaObject {
1286 string: Some(Box::new(StringValidation {
1287 max_length: None,
1288 min_length: None,
1289 pattern: Some(pattern),
1290 })),
1291 ..Default::default()
1292 })));
1293
1294 let additional_properties = Some(Box::new(
1296 validation
1297 .pattern_properties
1298 .values()
1299 .next()
1300 .cloned()
1301 .unwrap_or_else(|| unreachable!("pattern_properties cannot be empty here")),
1302 ));
1303
1304 let type_entry = self.make_map(
1305 type_name.into_option(),
1306 &property_names,
1307 &additional_properties,
1308 )?;
1309
1310 Ok((type_entry, metadata))
1311 }
1312
1313 None => {
1314 let type_entry = self.make_map(type_name.into_option(), &None, &None)?;
1315 Ok((type_entry, metadata))
1316 }
1317
1318 Some(validation) => {
1320 let tmp_type_name = get_type_name(&type_name, metadata);
1321 let (properties, deny_unknown_fields) =
1322 self.struct_members(tmp_type_name, validation)?;
1323
1324 Ok((
1325 TypeEntryStruct::from_metadata(
1326 self,
1327 type_name,
1328 metadata,
1329 properties,
1330 deny_unknown_fields,
1331 original_schema.clone(),
1332 ),
1333 &None,
1334 ))
1335 }
1336 }
1337 }
1338
1339 fn convert_reference<'a>(
1340 &self,
1341 metadata: &'a Option<Box<Metadata>>,
1342 ref_name: &str,
1343 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1344 if !ref_name.starts_with('#') {
1345 panic!("external references are not supported: {}", ref_name);
1346 }
1347 let key = ref_key(ref_name);
1348 let type_id = self
1349 .ref_to_id
1350 .get(&key)
1351 .unwrap_or_else(|| panic!("$ref {} is missing", ref_name));
1352 Ok((
1353 TypeEntryDetails::Reference(type_id.clone()).into(),
1354 metadata,
1355 ))
1356 }
1357
1358 fn convert_all_of<'a>(
1359 &mut self,
1360 type_name: Name,
1361 original_schema: &'a Schema,
1362 metadata: &'a Option<Box<Metadata>>,
1363 subschemas: &[Schema],
1364 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1365 debug!(
1366 "all_of {}",
1367 serde_json::to_string_pretty(subschemas).unwrap()
1368 );
1369 if let Some(ty) =
1370 self.maybe_singleton_subschema(type_name.clone(), original_schema, subschemas)
1371 {
1372 return Ok((ty, metadata));
1373 }
1374
1375 let merged_schema = merge_all(subschemas, &self.definitions);
1432 if let Schema::Bool(false) = &merged_schema {
1433 self.convert_never(type_name, original_schema)
1434 } else {
1435 let mut merged_schema = merged_schema.into_object();
1436 assert!(merged_schema.metadata.is_none());
1437 merged_schema.metadata = metadata.clone();
1438
1439 let (type_entry, _) =
1440 self.convert_schema_object(type_name, original_schema, &merged_schema)?;
1441 Ok((type_entry, &None))
1442 }
1443 }
1444
1445 fn convert_any_of<'a>(
1446 &mut self,
1447 type_name: Name,
1448 original_schema: &'a Schema,
1449 metadata: &'a Option<Box<Metadata>>,
1450 subschemas: &'a [Schema],
1451 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1452 if let Some(ty) = self.maybe_option(type_name.clone(), metadata, subschemas) {
1457 return Ok((ty, metadata));
1458 }
1459
1460 if all_mutually_exclusive(subschemas, &self.definitions) {
1464 self.convert_one_of(type_name, original_schema, metadata, subschemas)
1465 } else {
1466 self.flattened_union_struct(type_name, original_schema, metadata, subschemas, true)
1476 }
1477 }
1478
1479 pub(crate) fn convert_one_of<'a>(
1534 &mut self,
1535 type_name: Name,
1536 original_schema: &'a Schema,
1537 metadata: &'a Option<Box<schemars::schema::Metadata>>,
1538 subschemas: &'a [Schema],
1539 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1540 debug!(
1541 "one_of {}",
1542 serde_json::to_string_pretty(subschemas).unwrap()
1543 );
1544
1545 let ty = self
1556 .maybe_option(type_name.clone(), metadata, subschemas)
1557 .or_else(|| {
1558 self.maybe_externally_tagged_enum(
1559 type_name.clone(),
1560 original_schema,
1561 metadata,
1562 subschemas,
1563 )
1564 })
1565 .or_else(|| {
1566 self.maybe_adjacently_tagged_enum(
1567 type_name.clone(),
1568 original_schema,
1569 metadata,
1570 subschemas,
1571 )
1572 })
1573 .or_else(|| {
1574 self.maybe_internally_tagged_enum(
1575 type_name.clone(),
1576 original_schema,
1577 metadata,
1578 subschemas,
1579 )
1580 })
1581 .or_else(|| {
1582 self.maybe_singleton_subschema(type_name.clone(), original_schema, subschemas)
1583 })
1584 .map_or_else(
1585 || self.untagged_enum(type_name, original_schema, metadata, subschemas),
1586 Ok,
1587 )?;
1588
1589 Ok((ty, metadata))
1590 }
1591
1592 pub(crate) fn convert_not<'a>(
1609 &mut self,
1610 type_name: Name,
1611 original_schema: &'a Schema,
1612 metadata: &'a Option<Box<schemars::schema::Metadata>>,
1613 subschema: &'a Schema,
1614 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1615 match subschema {
1616 Schema::Bool(b) => {
1618 let (type_entry, _) = self.convert_schema(type_name, &Schema::Bool(!b))?;
1619 Ok((type_entry, metadata))
1620 }
1621
1622 Schema::Object(SchemaObject {
1624 metadata: _,
1625 instance_type: None,
1626 format: None,
1627 enum_values: None,
1628 const_value: None,
1629 subschemas: None,
1630 number: None,
1631 string: None,
1632 array: None,
1633 object: None,
1634 reference: None,
1635 extensions: _,
1636 }) => {
1637 let (type_entry, _) = self.convert_schema(type_name, &Schema::Bool(false))?;
1638 Ok((type_entry, metadata))
1639 }
1640
1641 Schema::Object(
1643 schema @ SchemaObject {
1644 instance_type: Some(SingleOrVec::Single(_)),
1645 enum_values: Some(enum_values),
1646 ..
1647 },
1648 ) => {
1649 let type_schema = SchemaObject {
1650 enum_values: None,
1651 ..schema.clone()
1652 };
1653
1654 let (type_entry, _) =
1655 self.convert_schema_object(Name::Unknown, original_schema, &type_schema)?;
1656
1657 enum_values
1661 .iter()
1662 .try_for_each(|value| type_entry.validate_value(self, value).map(|_| ()))?;
1663
1664 let type_id = self.assign_type(type_entry);
1665
1666 let newtype_entry = TypeEntryNewtype::from_metadata_with_deny_values(
1667 self,
1668 type_name,
1669 metadata,
1670 type_id,
1671 enum_values,
1672 original_schema.clone(),
1673 );
1674
1675 Ok((newtype_entry, metadata))
1676 }
1677
1678 Schema::Object(SchemaObject {
1680 metadata,
1681 instance_type: None,
1682 format: None,
1683 enum_values: Some(enum_values),
1684 const_value: None,
1685 subschemas: None,
1686 number: None,
1687 string: None,
1688 array: None,
1689 object: None,
1690 reference: None,
1691 extensions: _,
1692 }) => {
1693 let instance_types = enum_values
1695 .iter()
1696 .map(|v| match v {
1697 serde_json::Value::Bool(_) => InstanceType::Boolean,
1698 serde_json::Value::Number(_) => InstanceType::Number,
1699 serde_json::Value::String(_) => InstanceType::String,
1700
1701 serde_json::Value::Null
1702 | serde_json::Value::Array(_)
1703 | serde_json::Value::Object(_) => {
1704 panic!("unhandled type for `not` construction: {}", v)
1705 }
1706 })
1707 .collect::<BTreeSet<_>>();
1708
1709 match (instance_types.len(), instance_types.iter().next()) {
1710 (1, Some(instance_type)) => {
1711 let typed_schema = SchemaObject {
1712 instance_type: Some(schemars::schema::SingleOrVec::Single(Box::new(
1713 *instance_type,
1714 ))),
1715 ..Default::default()
1716 };
1717
1718 let (type_entry, _) = self.convert_schema_object(
1719 Name::Unknown,
1720 original_schema,
1721 &typed_schema,
1722 )?;
1723 enum_values.iter().try_for_each(|value| {
1727 type_entry.validate_value(self, value).map(|_| ())
1728 })?;
1729
1730 let type_id = self.assign_type(type_entry);
1731
1732 let newtype_entry = TypeEntryNewtype::from_metadata_with_deny_values(
1733 self,
1734 type_name,
1735 metadata,
1736 type_id,
1737 enum_values,
1738 original_schema.clone(),
1739 );
1740
1741 Ok((newtype_entry, metadata))
1742 }
1743
1744 _ => panic!(
1745 "multiple implied types for an un-typed enum {:?} {:?}",
1746 instance_types, enum_values,
1747 ),
1748 }
1749 }
1750
1751 _ => todo!("unhandled not schema {:#?}", subschema),
1752 }
1753 }
1754
1755 fn convert_array<'a>(
1756 &mut self,
1757 type_name: Name,
1758 metadata: &'a Option<Box<Metadata>>,
1759 validation: &ArrayValidation,
1760 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1761 match validation {
1762 ArrayValidation {
1767 items,
1768 additional_items,
1769 max_items: Some(max_items),
1770 min_items: Some(min_items),
1771 unique_items: None,
1772 contains: None,
1773 } if max_items == min_items && *max_items > 0 => match items {
1774 Some(SingleOrVec::Vec(items)) if items.len() < *max_items as usize => {
1776 let rest_name = type_name.append("additional");
1777 let rest_id = if let Some(rest_schema) = additional_items {
1778 self.id_for_schema(rest_name, rest_schema)?.0
1779 } else {
1780 self.id_for_schema(rest_name, &Schema::Bool(true))?.0
1781 };
1782 let start = items.iter().enumerate().map(|(ii, item_schema)| {
1783 let item_name = type_name.append(&format!("item{}", ii));
1784 Ok(self.id_for_schema(item_name, item_schema)?.0)
1785 });
1786 let rest = (items.len()..*max_items as usize).map(|_| Ok(rest_id.clone()));
1787 let types = start.chain(rest).collect::<Result<Vec<_>>>()?;
1788 Ok((TypeEntryDetails::Tuple(types).into(), metadata))
1789 }
1790 Some(SingleOrVec::Vec(items)) => {
1792 let types = items
1793 .iter()
1794 .take(*max_items as usize)
1795 .enumerate()
1796 .map(|(ii, item_schema)| {
1797 let item_name = type_name.append(&format!("item{}", ii));
1798 Ok(self.id_for_schema(item_name, item_schema)?.0)
1799 })
1800 .collect::<Result<_>>()?;
1801 Ok((TypeEntryDetails::Tuple(types).into(), metadata))
1802 }
1803
1804 Some(SingleOrVec::Single(item_schema)) => {
1806 let item_id = self.id_for_schema(type_name.append("item"), item_schema)?.0;
1807 Ok((
1808 TypeEntryDetails::Array(item_id, *max_items as usize).into(),
1809 metadata,
1810 ))
1811 }
1812 None => {
1814 let any_id = self
1815 .id_for_schema(type_name.append("item"), &Schema::Bool(true))?
1816 .0;
1817 Ok((
1818 TypeEntryDetails::Array(any_id, *max_items as usize).into(),
1819 metadata,
1820 ))
1821 }
1822 },
1823
1824 ArrayValidation {
1826 items: Some(SingleOrVec::Single(item)),
1827 additional_items: _, max_items: _, min_items: _, unique_items,
1831 contains: None,
1832 } => {
1833 let item_type_name = match get_type_name(&type_name, metadata) {
1834 Some(s) => Name::Suggested(format!("{}Item", s)),
1835 None => Name::Unknown,
1836 };
1837 let (type_id, _) = self.id_for_schema(item_type_name, item.as_ref())?;
1838
1839 match unique_items {
1841 Some(true) => Ok((TypeEntryDetails::Set(type_id).into(), metadata)),
1842 _ => Ok((TypeEntryDetails::Vec(type_id).into(), metadata)),
1843 }
1844 }
1845
1846 ArrayValidation {
1848 items: None,
1849 additional_items: _, max_items: _, min_items: _, unique_items,
1853 contains: None,
1854 } => {
1855 self.uses_serde_json = true;
1856 let type_id = self.assign_type(TypeEntryDetails::JsonValue.into());
1857
1858 match unique_items {
1860 Some(true) => Ok((TypeEntryDetails::Set(type_id).into(), metadata)),
1861 _ => Ok((TypeEntryDetails::Vec(type_id).into(), metadata)),
1862 }
1863 }
1864
1865 _ => Err(Error::InvalidSchema {
1866 type_name: type_name.into_option(),
1867 reason: format!("unhandled array validation {:#?}", validation),
1868 }),
1869 }
1870 }
1871
1872 fn convert_array_of_any<'a>(
1873 &mut self,
1874 metadata: &'a Option<Box<Metadata>>,
1875 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1876 self.uses_serde_json = true;
1877 let type_id = self.assign_type(TypeEntryDetails::JsonValue.into());
1878 Ok((TypeEntryDetails::Vec(type_id).into(), metadata))
1879 }
1880
1881 fn convert_bool<'a>(
1883 &self,
1884 metadata: &'a Option<Box<Metadata>>,
1885 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1886 Ok((TypeEntry::new_boolean(), metadata))
1887 }
1888
1889 fn convert_permissive<'a>(
1890 &mut self,
1891 metadata: &'a Option<Box<Metadata>>,
1892 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1893 self.uses_serde_json = true;
1894 Ok((TypeEntryDetails::JsonValue.into(), metadata))
1895 }
1896
1897 fn convert_never<'a>(
1898 &mut self,
1899 type_name: Name,
1900 schema: &'a Schema,
1901 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1902 let ty = TypeEntryEnum::from_metadata(
1903 self,
1904 type_name,
1905 &None,
1906 EnumTagType::External,
1907 vec![],
1908 true,
1909 schema.clone(),
1910 );
1911 Ok((ty, &None))
1912 }
1913
1914 fn convert_typed_enum<'a>(
1915 &mut self,
1916 type_name: Name,
1917 original_schema: &'a Schema,
1918 schema: &'a SchemaObject,
1919 enum_values: &[serde_json::Value],
1920 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1921 let type_schema = SchemaObject {
1922 enum_values: None,
1923 ..schema.clone()
1924 };
1925
1926 let inner_type_name = match get_type_name(&type_name, &schema.metadata) {
1927 Some(s) => Name::Suggested(format!("{}Inner", s)),
1928 None => Name::Unknown,
1929 };
1930
1931 let (type_entry, metadata) =
1932 self.convert_schema_object(inner_type_name, original_schema, &type_schema)?;
1933
1934 enum_values
1936 .iter()
1937 .try_for_each(|value| type_entry.validate_value(self, value).map(|_| ()))?;
1938
1939 let type_id = self.assign_type(type_entry);
1940
1941 let newtype_entry = TypeEntryNewtype::from_metadata_with_enum_values(
1942 self,
1943 type_name,
1944 metadata,
1945 type_id,
1946 enum_values,
1947 original_schema.clone(),
1948 );
1949
1950 Ok((
1951 newtype_entry,
1952 if metadata.is_some() {
1953 &schema.metadata
1954 } else {
1955 &None
1956 },
1957 ))
1958 }
1959
1960 fn convert_unknown_enum<'a>(
1961 &mut self,
1962 type_name: Name,
1963 original_schema: &'a Schema,
1964 metadata: &'a Option<Box<Metadata>>,
1965 enum_values: &[serde_json::Value],
1966 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
1967 if enum_values.is_empty() {
1968 return self.convert_never(type_name, original_schema);
1969 }
1970
1971 let mut instance_types = enum_values
1976 .iter()
1977 .map(|v| match v {
1978 serde_json::Value::Null => InstanceType::Null,
1979 serde_json::Value::Bool(_) => InstanceType::Boolean,
1980 serde_json::Value::Number(_) => InstanceType::Number,
1981 serde_json::Value::String(_) => InstanceType::String,
1982 serde_json::Value::Array(_) => InstanceType::Array,
1983 serde_json::Value::Object(_) => InstanceType::Object,
1984 })
1985 .collect::<BTreeSet<_>>();
1986
1987 let has_null = instance_types.remove(&InstanceType::Null);
1988
1989 if has_null {
1990 let enum_values = enum_values
1993 .iter()
1994 .filter(|v| !v.is_null())
1995 .cloned()
1996 .collect::<Vec<_>>();
1997
1998 if enum_values.is_empty() {
1999 self.convert_null(metadata)
2000 } else {
2001 let (type_entry, metadata) =
2002 self.convert_unknown_enum(type_name, original_schema, metadata, &enum_values)?;
2003 let type_entry = self.type_to_option(type_entry);
2004 Ok((type_entry, metadata))
2005 }
2006 } else {
2007 match (instance_types.len(), instance_types.iter().next()) {
2008 (1, Some(InstanceType::String)) => self.convert_enum_string(
2009 type_name,
2010 original_schema,
2011 metadata,
2012 enum_values,
2013 None,
2014 ),
2015
2016 (1, Some(InstanceType::Boolean)) => self.convert_bool(metadata),
2020
2021 (1, Some(instance_type)) => {
2022 let typed_schema = SchemaObject {
2023 instance_type: Some(schemars::schema::SingleOrVec::Single(Box::new(
2024 *instance_type,
2025 ))),
2026 ..Default::default()
2027 };
2028 let (type_entry, new_metadata) = self.convert_typed_enum(
2029 type_name,
2030 original_schema,
2031 &typed_schema,
2032 enum_values,
2033 )?;
2034 Ok((
2035 type_entry,
2036 if new_metadata.is_some() {
2037 metadata
2038 } else {
2039 &None
2040 },
2041 ))
2042 }
2043 (1, None) => unreachable!(),
2044 _ => panic!(
2045 "multiple implied types for an un-typed enum {:?} {:?}",
2046 instance_types, enum_values,
2047 ),
2048 }
2049 }
2050 }
2051
2052 pub(crate) fn convert_option<'a>(
2053 &mut self,
2054 type_name: Name,
2055 metadata: &'a Option<Box<Metadata>>,
2056 schema: &'_ Schema,
2057 ) -> Result<(TypeEntry, &'a Option<Box<Metadata>>)> {
2058 let (ty, _) = self.convert_schema(type_name, schema)?;
2059 let ty = self.type_to_option(ty);
2060
2061 Ok((ty, metadata))
2062 }
2063
2064 pub(crate) fn maybe_singleton_subschema(
2067 &mut self,
2068 type_name: Name,
2069 _original_schema: &Schema,
2070 subschemas: &[Schema],
2071 ) -> Option<TypeEntry> {
2072 match (subschemas.len(), subschemas.first()) {
2073 (1, Some(subschema)) => Some(self.convert_schema(type_name, subschema).ok()?.0),
2074 _ => None,
2075 }
2076 }
2077}
2078
2079#[cfg(test)]
2080mod tests {
2081 use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
2082
2083 use paste::paste;
2084 use quote::{quote, ToTokens};
2085 use schema::Schema;
2086 use schemars::{
2087 schema::{InstanceType, Metadata, NumberValidation, RootSchema, SchemaObject},
2088 schema_for, JsonSchema,
2089 };
2090 use serde_json::json;
2091
2092 use crate::{
2093 test_util::validate_output, Error, Name, TypeSpace, TypeSpaceImpl, TypeSpaceSettings,
2094 };
2095
2096 #[track_caller]
2097 fn int_helper<T: JsonSchema>(type_name: &'static str) {
2098 let schema = schema_for!(T);
2099
2100 let mut type_space = TypeSpace::default();
2101 type_space
2102 .add_ref_types(schema.definitions.clone())
2103 .unwrap();
2104 let (ty, _) = type_space
2105 .convert_schema_object(
2106 Name::Unknown,
2107 &schemars::schema::Schema::Object(schema.schema.clone()),
2108 &schema.schema,
2109 )
2110 .unwrap();
2111 let output = ty.type_name(&type_space);
2112 let actual = output.split("::").last().unwrap().trim();
2113 let expected = type_name.split("::").last().unwrap();
2114 assert_eq!(actual, expected);
2115 }
2116
2117 macro_rules! int_test {
2118 ($t:ty) => {
2119 paste! {
2120 #[test]
2121 fn [<test_int_ $t:lower>]() {
2122 int_helper::<$t>(stringify!($t))
2123 }
2124 }
2125 };
2126 }
2127
2128 int_test!(u8);
2129 int_test!(u16);
2130 int_test!(u32);
2131 int_test!(u64);
2132 int_test!(i8);
2133 int_test!(i16);
2134 int_test!(i32);
2135 int_test!(i64);
2136 int_test!(NonZeroU8);
2137 int_test!(NonZeroU16);
2138 int_test!(NonZeroU32);
2139 int_test!(NonZeroU64);
2140
2141 #[test]
2142 fn test_redundant_types() {
2143 #[derive(JsonSchema)]
2144 #[allow(dead_code)]
2145 struct Alphabet {
2146 a: u32,
2147 b: u32,
2148 c: u32,
2149 d: Option<u32>,
2150 e: Option<u32>,
2151 f: (u32, u32, u32, Option<u32>),
2152 }
2153
2154 let schema = schema_for!(Alphabet);
2155
2156 let mut type_space = TypeSpace::default();
2157 type_space
2158 .add_ref_types(schema.definitions.clone())
2159 .unwrap();
2160 let _ = type_space
2161 .add_type_with_name(&schema.schema.into(), Some("Alphabet".to_string()))
2162 .unwrap();
2163
2164 assert_eq!(type_space.iter_types().count(), 4);
2170 }
2171
2172 #[test]
2173 fn test_basic_option_flat() {
2174 #[derive(JsonSchema, Schema)]
2175 #[allow(dead_code)]
2176 struct C {}
2177
2178 #[derive(JsonSchema, Schema)]
2179 #[allow(dead_code)]
2180 struct A {
2181 a: Option<C>,
2182 }
2183
2184 validate_output::<A>();
2185 }
2186
2187 #[test]
2188 fn test_unit_option() {
2189 #[derive(JsonSchema, Schema)]
2190 #[allow(dead_code)]
2191 struct Foo;
2192
2193 #[derive(JsonSchema, Schema)]
2194 #[allow(dead_code)]
2195 struct Bar {
2196 a: Option<Foo>,
2197 }
2198
2199 validate_output::<Bar>();
2200 }
2201
2202 #[test]
2203 fn test_low_default() {
2204 let schema = SchemaObject {
2205 instance_type: Some(InstanceType::Integer.into()),
2206 format: Some("uint".to_string()),
2207 metadata: Some(
2208 Metadata {
2209 default: Some(json!(-1i32)),
2210 ..Default::default()
2211 }
2212 .into(),
2213 ),
2214 number: Some(NumberValidation::default().into()),
2215 ..Default::default()
2216 };
2217
2218 let mut type_space = TypeSpace::default();
2219 match type_space.convert_schema_object(
2220 Name::Unknown,
2221 &schemars::schema::Schema::Object(schema.clone()),
2222 &schema,
2223 ) {
2224 Err(Error::InvalidValue) => (),
2225 _ => panic!("unexpected result"),
2226 }
2227 }
2228
2229 #[test]
2230 fn test_high_default() {
2231 let schema = SchemaObject {
2232 instance_type: Some(InstanceType::Integer.into()),
2233 metadata: Some(
2234 Metadata {
2235 default: Some(json!(867_5309_u32)),
2236 ..Default::default()
2237 }
2238 .into(),
2239 ),
2240 number: Some(
2241 NumberValidation {
2242 maximum: Some(256.0),
2243 ..Default::default()
2244 }
2245 .into(),
2246 ),
2247 ..Default::default()
2248 };
2249
2250 let mut type_space = TypeSpace::default();
2251 match type_space.convert_schema_object(
2252 Name::Unknown,
2253 &schemars::schema::Schema::Object(schema.clone()),
2254 &schema,
2255 ) {
2256 Err(Error::InvalidValue) => (),
2257 _ => panic!("unexpected result"),
2258 }
2259 }
2260
2261 #[test]
2262 fn test_null() {
2263 let schema_json = r#"
2264 {
2265 "title": "Null",
2266 "type": "string",
2267 "enum": [null]
2268 }
2269 "#;
2270
2271 let schema: RootSchema = serde_json::from_str(schema_json).unwrap();
2272
2273 let mut type_space = TypeSpace::default();
2274 let _ = type_space.add_type(&schema.schema.into()).unwrap();
2275
2276 let actual = type_space.to_stream();
2277 let file = syn::parse2::<syn::File>(actual).expect("type space should emit a valid file");
2278 match file.items.as_slice() {
2279 [syn::Item::Mod(error)] if error.ident == "error" => {}
2280 _ => panic!("unexpected file contents {}", file.to_token_stream()),
2281 }
2282 }
2283
2284 #[test]
2285 fn test_overridden_conversion() {
2286 let schema_json = r#"
2287 {
2288 "description": "don't let this fool you",
2289 "type": "string",
2290 "format": "uuid"
2291 }
2292 "#;
2293
2294 let schema: RootSchema = serde_json::from_str(schema_json).unwrap();
2295
2296 let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_conversion(
2297 SchemaObject {
2298 instance_type: Some(InstanceType::String.into()),
2299 format: Some("uuid".to_string()),
2300 ..Default::default()
2301 },
2302 "not::a::real::library::Uuid",
2303 [TypeSpaceImpl::Display].into_iter(),
2304 ));
2305 let type_id = type_space.add_type(&schema.schema.into()).unwrap();
2306 let typ = type_space.get_type(&type_id).unwrap();
2307
2308 let actual = typ.ident();
2309 let expected = quote! { not::a::real::library::Uuid };
2310 assert_eq!(actual.to_string(), expected.to_string());
2311 }
2312}