typify_impl/
defaults.rs

1// Copyright 2025 Oxide Computer Company
2
3use 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
19// Implementations for "stock" default functions so we don't litter the
20// namespace with many that are effectively identical.
21impl 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        // Check the "whole-type" default.
65        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        // Check default values for struct properties or those of struct-type
89        // enum variants.
90        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    /// Check that the given [`Value`] is a valid instance of this type
136    ///
137    /// The return value indicates whether the default is the "intrinsic",
138    /// typical default for the given type, can be handled by generic function,
139    /// or requires a bespoke function to generate the value. This contains
140    /// additional validation logic compared with [`value()`] but is able to skip the parts where we actually emit code.
141    ///
142    /// [`Value`]: serde_json::Value
143    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                    // Make sure the default is valid for the sub-type.
182                    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                            // Sets can't contain duplicates; also Value isn't
231                            // Ord so O(n^2) it is!
232                            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                // This is tricky. There's not a lot we can do--particularly if
272                // and when we start to consider arbitrary types as "built-in"
273                // (e.g. if schemars tags types with an extension to denote
274                // their rust type or if the user can supply a list of type
275                // names to treat as built-in). So we just do no checking and
276                // will fail an `unwrap()` in the code emitted by `value()` if
277                // this Value is not valid for this built-in type.
278                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            // Note that min and max values are handled already by the
287            // conversion routines since we have those close at hand.
288            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    /// Return a string representing the function that can be called to produce
325    /// the value for the given default. If there is no such built-in function,
326    /// the .1 will be Some with a TokenStream for a function that can produce
327    /// that value.
328    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            // This can only be covered by the intrinsic default
337            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, &quote! { 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            // Make an object without the tag.
428            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        // The name of the variant is not meaningful; we just need to see
484        // if any of the variants are valid with the given default.
485        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    // Gather up all properties including those of flattened struct properties:
533    // a tuple of (name: Option<String>, type_id: TypeId, required: bool). We
534    // partition these into the named_properties which we then put into a map
535    // with the property name as the key, and unnamed_properties which consists
536    // of properties from flattened maps which have types but not names.
537    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    // These are the direct properties of this struct as well as the properties
543    // of any nested, flatted struct.
544    let named_properties = named_properties
545        .into_iter()
546        .map(|(name, type_id, required)| (name.unwrap(), (type_id, required)))
547        .collect::<BTreeMap<_, _>>();
548    // These are just the types for any flattened map (either within this
549    // struct or nested within another flattened struct).
550    let unnamed_properties = unnamed_properties
551        .into_iter()
552        .map(|(_, type_id, _)| type_id)
553        .collect::<Vec<_>>();
554
555    // Make sure that every value in the map validates properly.
556    map.iter().try_for_each(|(name, default_value)| {
557        // If there's a matching, named property, the value needs to validate.
558        // Otherwise it needs to validate against the schema of one of the
559        // unnamed properties i.e. it must be a valid value type for a nested,
560        // flatted map.
561        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    // Make sure that every required field is present in the map.
574    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        // The type must be a struct, an option for a struct, or a map.
601        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            // TODO Rather than an option, this should probably be something
620            // that lets us say "explicit name" or "type to validate against"
621            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}