1use std::collections::BTreeMap;
4
5use proc_macro2::TokenStream;
6use quote::{format_ident, quote};
7
8use crate::{
9 convert::STD_NUM_NONZERO_PREFIX,
10 type_entry::{
11 DefaultKind, EnumTagType, StructProperty, StructPropertyRename, StructPropertyState,
12 TypeEntry, TypeEntryDetails, TypeEntryEnum, TypeEntryNewtype, TypeEntryStruct, Variant,
13 VariantDetails, WrappedValue,
14 },
15 util::{sanitize, Case},
16 DefaultImpl, Error, Result, TypeId, TypeSpace,
17};
18
19impl From<&DefaultImpl> for TokenStream {
22 fn from(default: &DefaultImpl) -> Self {
23 match default {
24 DefaultImpl::Boolean => quote! {
25 pub(super) fn default_bool<const V: bool>() -> bool {
26 V
27 }
28 },
29 DefaultImpl::I64 => quote! {
30 pub(super) fn default_i64<T, const V: i64>() -> T
31 where
32 T: ::std::convert::TryFrom<i64>,
33 <T as ::std::convert::TryFrom<i64>>::Error: ::std::fmt::Debug,
34 {
35 T::try_from(V).unwrap()
36 }
37 },
38 DefaultImpl::U64 => quote! {
39 pub(super) fn default_u64<T, const V: u64>() -> T
40 where
41 T: ::std::convert::TryFrom<u64>,
42 <T as ::std::convert::TryFrom<u64>>::Error: ::std::fmt::Debug,
43 {
44 T::try_from(V).unwrap()
45 }
46 },
47 DefaultImpl::NZU64 => quote! {
48 pub(super) fn default_nzu64<T, const V: u64>() -> T
49 where
50 T: ::std::convert::TryFrom<::std::num::NonZeroU64>,
51 <T as ::std::convert::TryFrom<::std::num::NonZeroU64>>::Error:
52 ::std::fmt::Debug,
53 {
54 T::try_from(::std::num::NonZeroU64::try_from(V).unwrap())
55 .unwrap()
56 }
57 },
58 }
59 }
60}
61
62impl TypeEntry {
63 pub(crate) fn check_defaults(&self, type_space: &mut TypeSpace) -> Result<()> {
64 match &self.details {
66 TypeEntryDetails::Enum(TypeEntryEnum {
67 default: Some(WrappedValue(default)),
68 ..
69 })
70 | TypeEntryDetails::Struct(TypeEntryStruct {
71 default: Some(WrappedValue(default)),
72 ..
73 })
74 | TypeEntryDetails::Newtype(TypeEntryNewtype {
75 default: Some(WrappedValue(default)),
76 ..
77 }) => {
78 if let DefaultKind::Generic(default_fn) =
79 self.validate_value(type_space, default)?
80 {
81 type_space.defaults.insert(default_fn);
82 }
83 }
84
85 _ => (),
86 }
87
88 match &self.details {
91 TypeEntryDetails::Struct(TypeEntryStruct { properties, .. }) => {
92 properties
93 .iter()
94 .try_for_each(|prop| Self::check_property_defaults(prop, type_space))?;
95 }
96
97 TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
98 variants.iter().try_for_each(|variant| {
99 if let VariantDetails::Struct(properties) = &variant.details {
100 properties
101 .iter()
102 .try_for_each(|prop| Self::check_property_defaults(prop, type_space))
103 } else {
104 Ok(())
105 }
106 })?;
107 }
108
109 _ => (),
110 };
111
112 Ok(())
113 }
114
115 fn check_property_defaults(
116 property: &StructProperty,
117 type_space: &mut TypeSpace,
118 ) -> Result<()> {
119 if let StructProperty {
120 state: StructPropertyState::Default(WrappedValue(prop_default)),
121 type_id,
122 ..
123 } = property
124 {
125 let type_entry = type_space.id_to_entry.get(type_id).unwrap();
126 if let DefaultKind::Generic(default_fn) =
127 type_entry.validate_value(type_space, prop_default)?
128 {
129 type_space.defaults.insert(default_fn);
130 }
131 }
132 Ok(())
133 }
134
135 pub(crate) fn validate_value(
144 &self,
145 type_space: &TypeSpace,
146 default: &serde_json::Value,
147 ) -> Result<DefaultKind> {
148 match &self.details {
149 TypeEntryDetails::Enum(TypeEntryEnum {
150 tag_type, variants, ..
151 }) => match tag_type {
152 EnumTagType::External => {
153 validate_default_for_external_enum(type_space, variants, default)
154 .ok_or_else(Error::invalid_value)
155 }
156 EnumTagType::Internal { tag } => {
157 validate_default_for_internal_enum(type_space, variants, default, tag)
158 .ok_or_else(Error::invalid_value)
159 }
160 EnumTagType::Adjacent { tag, content } => {
161 validate_default_for_adjacent_enum(type_space, variants, default, tag, content)
162 .ok_or_else(Error::invalid_value)
163 }
164 EnumTagType::Untagged => {
165 validate_default_for_untagged_enum(type_space, variants, default)
166 .ok_or_else(Error::invalid_value)
167 }
168 },
169 TypeEntryDetails::Struct(TypeEntryStruct { properties, .. }) => {
170 validate_default_struct_props(properties, type_space, default)
171 .ok_or_else(Error::invalid_value)
172 }
173
174 TypeEntryDetails::Newtype(TypeEntryNewtype { type_id, .. }) => {
175 validate_type_id(type_id, type_space, default)
176 }
177 TypeEntryDetails::Option(type_id) => {
178 if let serde_json::Value::Null = default {
179 Ok(DefaultKind::Intrinsic)
180 } else {
181 let _ = validate_type_id(type_id, type_space, default)?;
183 Ok(DefaultKind::Specific)
184 }
185 }
186 TypeEntryDetails::Box(type_id) => validate_type_id(type_id, type_space, default),
187
188 TypeEntryDetails::Vec(type_id) => {
189 if let serde_json::Value::Array(v) = default {
190 if v.is_empty() {
191 Ok(DefaultKind::Intrinsic)
192 } else {
193 let type_entry = type_space.id_to_entry.get(type_id).unwrap();
194 for value in v {
195 let _ = type_entry.validate_value(type_space, value)?;
196 }
197 Ok(DefaultKind::Specific)
198 }
199 } else {
200 Err(Error::invalid_value())
201 }
202 }
203 TypeEntryDetails::Map(key_id, value_id) => {
204 if let serde_json::Value::Object(m) = default {
205 if m.is_empty() {
206 Ok(DefaultKind::Intrinsic)
207 } else {
208 let key_ty = type_space.id_to_entry.get(key_id).unwrap();
209 let value_ty = type_space.id_to_entry.get(value_id).unwrap();
210 for (key, value) in m {
211 let _ = key_ty.validate_value(
212 type_space,
213 &serde_json::Value::String(key.clone()),
214 )?;
215 let _ = value_ty.validate_value(type_space, value)?;
216 }
217 Ok(DefaultKind::Specific)
218 }
219 } else {
220 Err(Error::invalid_value())
221 }
222 }
223 TypeEntryDetails::Set(type_id) => {
224 if let serde_json::Value::Array(v) = default {
225 if v.is_empty() {
226 Ok(DefaultKind::Intrinsic)
227 } else {
228 let type_entry = type_space.id_to_entry.get(type_id).unwrap();
229 for (i, value) in v.iter().enumerate() {
230 for other in &v[(i + 1)..] {
233 if value == other {
234 return Err(Error::invalid_value());
235 }
236 }
237 let _ = type_entry.validate_value(type_space, value)?;
238 }
239 Ok(DefaultKind::Specific)
240 }
241 } else {
242 Err(Error::invalid_value())
243 }
244 }
245 TypeEntryDetails::Tuple(ids) => {
246 validate_default_tuple(ids, type_space, default).ok_or_else(Error::invalid_value)
247 }
248
249 TypeEntryDetails::Array(type_id, length) => {
250 let Some(arr) = default.as_array() else {
251 return Err(Error::invalid_value());
252 };
253 if arr.len() != *length {
254 return Err(Error::invalid_value());
255 }
256
257 let type_entry = type_space.id_to_entry.get(type_id).unwrap();
258 for value in arr {
259 let _ = type_entry.validate_value(type_space, value)?;
260 }
261 Ok(DefaultKind::Specific)
262 }
263 TypeEntryDetails::Unit => {
264 if let serde_json::Value::Null = default {
265 Ok(DefaultKind::Intrinsic)
266 } else {
267 Err(Error::invalid_value())
268 }
269 }
270 TypeEntryDetails::Native(_) => {
271 Ok(DefaultKind::Specific)
279 }
280 TypeEntryDetails::JsonValue => Ok(DefaultKind::Specific),
281 TypeEntryDetails::Boolean => match default {
282 serde_json::Value::Bool(false) => Ok(DefaultKind::Intrinsic),
283 serde_json::Value::Bool(true) => Ok(DefaultKind::Generic(DefaultImpl::Boolean)),
284 _ => Err(Error::invalid_value()),
285 },
286 TypeEntryDetails::Integer(itype) => match (default.as_u64(), default.as_i64()) {
289 (None, None) => Err(Error::invalid_value()),
290 (Some(0), _) => Ok(DefaultKind::Intrinsic),
291 (_, Some(0)) => unreachable!(),
292 (Some(_), _) => {
293 if itype.starts_with(STD_NUM_NONZERO_PREFIX) {
294 Ok(DefaultKind::Generic(DefaultImpl::NZU64))
295 } else {
296 Ok(DefaultKind::Generic(DefaultImpl::U64))
297 }
298 }
299 (_, Some(_)) => Ok(DefaultKind::Generic(DefaultImpl::I64)),
300 },
301 TypeEntryDetails::Float(_) => {
302 if let Some(value) = default.as_f64() {
303 if value == 0.0 {
304 Ok(DefaultKind::Intrinsic)
305 } else {
306 Ok(DefaultKind::Generic(DefaultImpl::I64))
307 }
308 } else {
309 Err(Error::invalid_value())
310 }
311 }
312 TypeEntryDetails::String => {
313 if let Some("") = default.as_str() {
314 Ok(DefaultKind::Intrinsic)
315 } else {
316 Ok(DefaultKind::Specific)
317 }
318 }
319
320 TypeEntryDetails::Reference(_) => unreachable!(),
321 }
322 }
323
324 pub(crate) fn default_fn(
329 &self,
330 default: &serde_json::Value,
331 type_space: &TypeSpace,
332 type_name: &str,
333 prop_name: &str,
334 ) -> (String, Option<TokenStream>) {
335 let maybe_builtin = match &self.details {
336 TypeEntryDetails::Unit => unreachable!(),
338 TypeEntryDetails::Boolean => Some("defaults::default_bool::<true>".to_string()),
339 TypeEntryDetails::Integer(name) => {
340 if let Some(value) = default.as_u64() {
341 if name.starts_with(STD_NUM_NONZERO_PREFIX) {
342 Some(format!("defaults::default_nzu64::<{}, {}>", name, value))
343 } else {
344 Some(format!("defaults::default_u64::<{}, {}>", name, value))
345 }
346 } else if let Some(value) = default.as_i64() {
347 Some(format!("defaults::default_i64::<{}, {}>", name, value))
348 } else {
349 panic!()
350 }
351 }
352 _ => None,
353 };
354
355 if let Some(fn_name) = maybe_builtin {
356 (fn_name, None)
357 } else {
358 let n = self.type_ident(type_space, &Some("super".to_string()));
359 let value = self
360 .output_value(type_space, default, "e! { super:: })
361 .unwrap_or_else(|| {
362 panic!(
363 "{}\nvalue: {}\ntype: {:#?}",
364 "The default value could not be rendered for this type",
365 serde_json::to_string_pretty(default).unwrap(),
366 self,
367 )
368 });
369 let fn_name = sanitize(&format!("{}_{}", type_name, prop_name), Case::Snake);
370 let fn_ident = format_ident!("{}", fn_name);
371 let def = quote! {
372 pub(super) fn #fn_ident() -> #n {
373 #value
374 }
375 };
376 (format!("defaults::{}", fn_name), Some(def))
377 }
378 }
379}
380
381pub(crate) fn validate_default_for_external_enum(
382 type_space: &TypeSpace,
383 variants: &[Variant],
384 default: &serde_json::Value,
385) -> Option<DefaultKind> {
386 if let Some(simple_name) = default.as_str() {
387 let variant = variants
388 .iter()
389 .find(|variant| simple_name == variant.raw_name)?;
390 matches!(&variant.details, VariantDetails::Simple).then(|| ())?;
391
392 Some(DefaultKind::Specific)
393 } else {
394 let map = default.as_object()?;
395 if map.len() != 1 {
396 return None;
397 }
398
399 let (name, value) = map.iter().next()?;
400
401 let variant = variants.iter().find(|variant| name == &variant.raw_name)?;
402
403 match &variant.details {
404 VariantDetails::Simple => None,
405 VariantDetails::Item(type_id) => validate_type_id(type_id, type_space, value).ok(),
406 VariantDetails::Tuple(tup) => validate_default_tuple(tup, type_space, value),
407 VariantDetails::Struct(props) => {
408 validate_default_struct_props(props, type_space, value)
409 }
410 }
411 }
412}
413
414pub(crate) fn validate_default_for_internal_enum(
415 type_space: &TypeSpace,
416 variants: &[Variant],
417 default: &serde_json::Value,
418 tag: &str,
419) -> Option<DefaultKind> {
420 let map = default.as_object()?;
421 let name = map.get(tag).and_then(serde_json::Value::as_str)?;
422 let variant = variants.iter().find(|variant| name == variant.raw_name)?;
423
424 match &variant.details {
425 VariantDetails::Simple => Some(DefaultKind::Specific),
426 VariantDetails::Struct(props) => {
427 let inner_default = serde_json::Value::Object(
429 map.clone()
430 .into_iter()
431 .filter(|(name, _)| name != tag)
432 .collect(),
433 );
434
435 validate_default_struct_props(props, type_space, &inner_default)
436 }
437
438 VariantDetails::Item(_) | VariantDetails::Tuple(_) => unreachable!(),
439 }
440}
441
442pub(crate) fn validate_default_for_adjacent_enum(
443 type_space: &TypeSpace,
444 variants: &[Variant],
445 default: &serde_json::Value,
446 tag: &str,
447 content: &str,
448) -> Option<DefaultKind> {
449 let map = default.as_object()?;
450
451 let (tag_value, content_value) = match (
452 map.len(),
453 map.get(tag).and_then(serde_json::Value::as_str),
454 map.get(content),
455 ) {
456 (1, Some(tag_value), None) => (tag_value, None),
457 (2, Some(tag_value), content_value @ Some(_)) => (tag_value, content_value),
458 _ => return None,
459 };
460
461 let variant = variants
462 .iter()
463 .find(|variant| tag_value == variant.raw_name)?;
464
465 match (&variant.details, content_value) {
466 (VariantDetails::Simple, None) => Some(DefaultKind::Specific),
467 (VariantDetails::Tuple(tup), Some(content_value)) => {
468 validate_default_tuple(tup, type_space, content_value)
469 }
470 (VariantDetails::Struct(props), Some(content_value)) => {
471 validate_default_struct_props(props, type_space, content_value)
472 }
473 _ => None,
474 }
475}
476
477pub(crate) fn validate_default_for_untagged_enum(
478 type_space: &TypeSpace,
479 variants: &[Variant],
480 default: &serde_json::Value,
481) -> Option<DefaultKind> {
482 variants.iter().find_map(|variant| {
483 match &variant.details {
486 VariantDetails::Simple => {
487 default.as_null()?;
488 Some(DefaultKind::Specific)
489 }
490 VariantDetails::Item(type_id) => validate_type_id(type_id, type_space, default).ok(),
491 VariantDetails::Tuple(tup) => validate_default_tuple(tup, type_space, default),
492 VariantDetails::Struct(props) => {
493 validate_default_struct_props(props, type_space, default)
494 }
495 }
496 })
497}
498
499fn validate_type_id(
500 type_id: &TypeId,
501 type_space: &TypeSpace,
502 default: &serde_json::Value,
503) -> Result<DefaultKind> {
504 let type_entry = type_space.id_to_entry.get(type_id).unwrap();
505 type_entry.validate_value(type_space, default)
506}
507
508fn validate_default_tuple(
509 types: &[TypeId],
510 type_space: &TypeSpace,
511 default: &serde_json::Value,
512) -> Option<DefaultKind> {
513 let arr = default.as_array()?;
514 if arr.len() != types.len() {
515 return None;
516 }
517
518 types
519 .iter()
520 .zip(arr.iter())
521 .all(|(type_id, value)| validate_type_id(type_id, type_space, value).is_ok())
522 .then_some(DefaultKind::Specific)
523}
524
525fn validate_default_struct_props(
526 properties: &[StructProperty],
527 type_space: &TypeSpace,
528 default: &serde_json::Value,
529) -> Option<DefaultKind> {
530 let map = default.as_object()?;
531
532 let (named_properties, unnamed_properties): (Vec<_>, Vec<_>) = properties
538 .iter()
539 .flat_map(|property| all_props(property, type_space))
540 .partition(|(name, _, _)| name.is_some());
541
542 let named_properties = named_properties
545 .into_iter()
546 .map(|(name, type_id, required)| (name.unwrap(), (type_id, required)))
547 .collect::<BTreeMap<_, _>>();
548 let unnamed_properties = unnamed_properties
551 .into_iter()
552 .map(|(_, type_id, _)| type_id)
553 .collect::<Vec<_>>();
554
555 map.iter().try_for_each(|(name, default_value)| {
557 if let Some((type_id, _)) = named_properties.get(name) {
562 validate_type_id(type_id, type_space, default_value)
563 .ok()
564 .map(|_| ())
565 } else {
566 unnamed_properties
567 .iter()
568 .any(|type_id| validate_type_id(type_id, type_space, default_value).is_ok())
569 .then_some(())
570 }
571 })?;
572
573 named_properties
575 .iter()
576 .filter(|(_, (_, required))| *required)
577 .try_for_each(|(name, _)| map.get(*name).map(|_| ()))?;
578
579 Some(DefaultKind::Specific)
580}
581
582fn all_props<'a>(
583 property: &'a StructProperty,
584 type_space: &'a TypeSpace,
585) -> Vec<(Option<&'a String>, &'a TypeId, bool)> {
586 let maybe_name = match &property.rename {
587 StructPropertyRename::None => Some(&property.name),
588 StructPropertyRename::Rename(rename) => Some(rename),
589 StructPropertyRename::Flatten => None,
590 };
591
592 if let Some(name) = maybe_name {
593 let required = match &property.state {
594 StructPropertyState::Required => true,
595 StructPropertyState::Optional | StructPropertyState::Default(_) => false,
596 };
597
598 vec![(Some(name), &property.type_id, required)]
599 } else {
600 let type_entry = type_space.id_to_entry.get(&property.type_id).unwrap();
602
603 let (properties, all_required) = match &type_entry.details {
604 TypeEntryDetails::Struct(TypeEntryStruct { properties, .. }) => {
605 let optional = matches!(&property.state, StructPropertyState::Optional);
606 (properties, !optional)
607 }
608 TypeEntryDetails::Option(type_id) => {
609 let type_entry = type_space.id_to_entry.get(type_id).unwrap();
610 if let TypeEntryDetails::Struct(TypeEntryStruct { properties, .. }) =
611 &type_entry.details
612 {
613 (properties, false)
614 } else {
615 unreachable!()
616 }
617 }
618
619 TypeEntryDetails::Map(_, value_id) => return vec![(None, value_id, false)],
622 _ => unreachable!(),
623 };
624
625 properties
626 .iter()
627 .flat_map(|property| all_props(property, type_space))
628 .map(|(name, type_id, required)| (name, type_id, required && all_required))
629 .collect()
630 }
631}
632
633#[cfg(test)]
634mod tests {
635 use std::collections::HashMap;
636
637 use schemars::JsonSchema;
638 use serde_json::json;
639 use uuid::Uuid;
640
641 use crate::{
642 test_util::get_type,
643 type_entry::{DefaultKind, TypeEntry},
644 DefaultImpl,
645 };
646
647 #[test]
648 fn test_default_option() {
649 let (type_space, type_id) = get_type::<Option<u32>>();
650 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
651
652 assert!(type_entry
653 .validate_value(&type_space, &json!("forty-two"))
654 .is_err());
655 assert!(matches!(
656 type_entry.validate_value(&type_space, &json!(null)),
657 Ok(DefaultKind::Intrinsic)
658 ));
659 assert!(matches!(
660 type_entry.validate_value(&type_space, &json!(42)),
661 Ok(DefaultKind::Specific)
662 ));
663 }
664
665 #[test]
666 fn test_default_box() {
667 let (type_space, type_id) = get_type::<Option<u32>>();
668
669 let type_entry = TypeEntry {
670 details: crate::type_entry::TypeEntryDetails::Box(type_id),
671 extra_derives: Default::default(),
672 };
673
674 assert!(type_entry
675 .validate_value(&type_space, &json!("forty-two"))
676 .is_err());
677 assert!(matches!(
678 type_entry.validate_value(&type_space, &json!(null)),
679 Ok(DefaultKind::Intrinsic)
680 ));
681 assert!(matches!(
682 type_entry.validate_value(&type_space, &json!(42)),
683 Ok(DefaultKind::Specific)
684 ));
685 }
686
687 #[test]
688 fn test_default_array() {
689 let (type_space, type_id) = get_type::<Vec<u32>>();
690 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
691
692 assert!(type_entry
693 .validate_value(&type_space, &json!([null]))
694 .is_err());
695 assert!(matches!(
696 type_entry.validate_value(&type_space, &json!([])),
697 Ok(DefaultKind::Intrinsic),
698 ));
699 assert!(matches!(
700 type_entry.validate_value(&type_space, &json!([1, 2, 5])),
701 Ok(DefaultKind::Specific),
702 ));
703 }
704
705 #[test]
706 fn test_default_map() {
707 let (type_space, type_id) = get_type::<HashMap<String, u32>>();
708 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
709
710 assert!(type_entry.validate_value(&type_space, &json!([])).is_err());
711 assert!(matches!(
712 type_entry.validate_value(&type_space, &json!({})),
713 Ok(DefaultKind::Intrinsic),
714 ));
715 assert!(matches!(
716 type_entry.validate_value(&type_space, &json!({"a": 1, "b": 2})),
717 Ok(DefaultKind::Specific),
718 ));
719 }
720
721 #[test]
722 fn test_default_tuple() {
723 let (type_space, type_id) = get_type::<(u32, u32, String)>();
724 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
725
726 assert!(type_entry
727 .validate_value(&type_space, &json!([1, 2, "three", 4]))
728 .is_err());
729 assert!(matches!(
730 type_entry.validate_value(&type_space, &json!([1, 2, "three"])),
731 Ok(DefaultKind::Specific),
732 ));
733 }
734
735 #[test]
736 fn test_default_builtin() {
737 let (type_space, type_id) = get_type::<Uuid>();
738 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
739
740 assert!(matches!(
741 type_entry.validate_value(&type_space, &json!("not-a-uuid")),
742 Ok(DefaultKind::Specific)
743 ));
744 }
745
746 #[test]
747 fn test_default_bool() {
748 let (type_space, type_id) = get_type::<bool>();
749 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
750
751 assert!(matches!(
752 type_entry.validate_value(&type_space, &json!(false)),
753 Ok(DefaultKind::Intrinsic),
754 ));
755 assert!(matches!(
756 type_entry.validate_value(&type_space, &json!(true)),
757 Ok(DefaultKind::Generic(DefaultImpl::Boolean)),
758 ));
759 }
760
761 #[test]
762 fn test_default_numbers_and_string() {
763 let (type_space, type_id) = get_type::<u32>();
764 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
765
766 assert!(type_entry
767 .validate_value(&type_space, &json!(true))
768 .is_err());
769 assert!(matches!(
770 type_entry.validate_value(&type_space, &json!(0)),
771 Ok(DefaultKind::Intrinsic),
772 ));
773 assert!(matches!(
774 type_entry.validate_value(&type_space, &json!(42)),
775 Ok(DefaultKind::Generic(DefaultImpl::U64)),
776 ));
777
778 let (type_space, type_id) = get_type::<String>();
779 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
780
781 assert!(matches!(
782 type_entry.validate_value(&type_space, &json!("")),
783 Ok(DefaultKind::Intrinsic),
784 ));
785 assert!(matches!(
786 type_entry.validate_value(&type_space, &json!("howdy")),
787 Ok(DefaultKind::Specific),
788 ));
789 }
790
791 #[test]
792 fn test_struct_simple() {
793 #[derive(JsonSchema)]
794 #[allow(dead_code)]
795 struct Test {
796 a: String,
797 b: u32,
798 c: Option<String>,
799 d: Option<f64>,
800 }
801
802 let (type_space, type_id) = get_type::<Test>();
803 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
804
805 assert!(matches!(
806 type_entry.validate_value(
807 &type_space,
808 &json!(
809 {
810 "a": "aaaa",
811 "b": 7,
812 "c": "cccc"
813 }
814 )
815 ),
816 Ok(DefaultKind::Specific),
817 ));
818 assert!(type_entry
819 .validate_value(
820 &type_space,
821 &json!(
822 {
823 "a": "aaaa",
824 "c": "cccc",
825 "d": 7
826 }
827 )
828 )
829 .is_err());
830 assert!(type_entry
831 .validate_value(
832 &type_space,
833 &json!(
834 {
835 "a": "aaaa",
836 "b": 7,
837 "d": {}
838 }
839 )
840 )
841 .is_err());
842 }
843
844 #[test]
845 fn test_enum_external() {
846 #[derive(JsonSchema)]
847 #[allow(dead_code)]
848 enum Test {
849 A,
850 B(String, String),
851 C { cc: String, dd: String },
852 }
853
854 let (type_space, type_id) = get_type::<Test>();
855 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
856
857 assert!(matches!(
858 type_entry.validate_value(&type_space, &json!("A")),
859 Ok(DefaultKind::Specific),
860 ));
861 assert!(matches!(
862 type_entry.validate_value(
863 &type_space,
864 &json!({
865 "B": ["xx", "yy"]
866 })
867 ),
868 Ok(DefaultKind::Specific),
869 ));
870 assert!(matches!(
871 type_entry.validate_value(
872 &type_space,
873 &json!({
874 "C": { "cc": "xx", "dd": "yy" }
875 })
876 ),
877 Ok(DefaultKind::Specific),
878 ));
879 assert!(type_entry
880 .validate_value(&type_space, &json!({ "A": null }))
881 .is_err());
882 assert!(type_entry.validate_value(&type_space, &json!("B")).is_err());
883 }
884
885 #[test]
886 fn test_enum_internal() {
887 #[derive(JsonSchema)]
888 #[allow(dead_code)]
889 #[serde(tag = "tag")]
890 enum Test {
891 A,
892 C { cc: String, dd: String },
893 }
894
895 let (type_space, type_id) = get_type::<Test>();
896 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
897
898 assert!(matches!(
899 type_entry.validate_value(
900 &type_space,
901 &json!({
902 "tag": "A"
903 })
904 ),
905 Ok(DefaultKind::Specific),
906 ));
907 assert!(matches!(
908 type_entry.validate_value(
909 &type_space,
910 &json!({
911 "tag": "C",
912 "cc": "xx",
913 "dd": "yy"
914 })
915 ),
916 Ok(DefaultKind::Specific),
917 ));
918 assert!(type_entry
919 .validate_value(
920 &type_space,
921 &json!({
922 "not-tag": "A"
923 })
924 )
925 .is_err());
926 assert!(type_entry
927 .validate_value(
928 &type_space,
929 &json!({
930 "tag": "B",
931 "cc": "where's D?"
932 })
933 )
934 .is_err());
935 }
936
937 #[test]
938 fn test_enum_adjacent() {
939 #[derive(JsonSchema)]
940 #[allow(dead_code)]
941 #[serde(tag = "tag", content = "content")]
942 enum Test {
943 A,
944 B(String, String),
945 C { cc: String, dd: String },
946 }
947
948 let (type_space, type_id) = get_type::<Test>();
949 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
950
951 assert!(matches!(
952 type_entry.validate_value(
953 &type_space,
954 &json!({
955 "tag": "A"
956 })
957 ),
958 Ok(DefaultKind::Specific),
959 ));
960 assert!(matches!(
961 type_entry.validate_value(
962 &type_space,
963 &json!({
964 "tag": "B",
965 "content": ["xx", "yy"]
966 })
967 ),
968 Ok(DefaultKind::Specific),
969 ));
970 assert!(matches!(
971 type_entry.validate_value(
972 &type_space,
973 &json!({
974 "tag": "C",
975 "content": { "cc": "xx", "dd": "yy" }
976 })
977 ),
978 Ok(DefaultKind::Specific),
979 ));
980 assert!(type_entry.validate_value(&type_space, &json!("A")).is_err());
981 assert!(type_entry
982 .validate_value(
983 &type_space,
984 &json!({
985 "tag": "A",
986 "content": null,
987 })
988 )
989 .is_err());
990 }
991 #[test]
992 fn test_enum_untagged() {
993 #[derive(JsonSchema)]
994 #[allow(dead_code)]
995 #[serde(untagged)]
996 enum Test {
997 A,
998 B(String, String),
999 C { cc: String, dd: String },
1000 }
1001
1002 let (type_space, type_id) = get_type::<Test>();
1003 let type_entry = type_space.id_to_entry.get(&type_id).unwrap();
1004
1005 assert!(matches!(
1006 type_entry.validate_value(&type_space, &json!(null)),
1007 Ok(DefaultKind::Specific),
1008 ));
1009 assert!(matches!(
1010 type_entry.validate_value(&type_space, &json!(["xx", "yy"])),
1011 Ok(DefaultKind::Specific),
1012 ));
1013 assert!(matches!(
1014 type_entry.validate_value(&type_space, &json!( { "cc": "xx", "dd": "yy" })),
1015 Ok(DefaultKind::Specific),
1016 ));
1017 assert!(type_entry.validate_value(&type_space, &json!({})).is_err());
1018 }
1019}