Skip to main content

typify_impl/
lib.rs

1// Copyright 2025 Oxide Computer Company
2
3//! typify backend implementation.
4
5#![deny(missing_docs)]
6
7use std::collections::{BTreeMap, BTreeSet};
8
9use conversions::SchemaCache;
10use log::info;
11use output::OutputSpace;
12use proc_macro2::TokenStream;
13use quote::{format_ident, quote, ToTokens};
14use schemars::schema::{Metadata, RootSchema, Schema};
15use thiserror::Error;
16use type_entry::{
17    StructPropertyState, TypeEntry, TypeEntryDetails, TypeEntryNative, TypeEntryNewtype,
18    WrappedValue,
19};
20
21use crate::util::{sanitize, Case};
22
23#[cfg(test)]
24mod test_util;
25
26mod conversions;
27mod convert;
28mod cycles;
29mod defaults;
30mod enums;
31mod merge;
32mod output;
33mod rust_extension;
34mod structs;
35mod type_entry;
36mod util;
37mod validate;
38mod value;
39
40#[allow(missing_docs)]
41#[derive(Error, Debug)]
42pub enum Error {
43    #[error("unexpected value type")]
44    BadValue(String, serde_json::Value),
45    #[error("invalid TypeId")]
46    InvalidTypeId,
47    #[error("value does not conform to the given schema")]
48    InvalidValue,
49    #[error("invalid schema for {}: {reason}", show_type_name(.type_name.as_deref()))]
50    InvalidSchema {
51        type_name: Option<String>,
52        reason: String,
53    },
54}
55
56impl Error {
57    fn invalid_value() -> Self {
58        Self::InvalidValue
59    }
60}
61
62#[allow(missing_docs)]
63pub type Result<T> = std::result::Result<T, Error>;
64
65fn show_type_name(type_name: Option<&str>) -> &str {
66    type_name.unwrap_or("<unknown type>")
67}
68
69/// Representation of a type which may have a definition or may be built-in.
70#[derive(Debug)]
71pub struct Type<'a> {
72    type_space: &'a TypeSpace,
73    type_entry: &'a TypeEntry,
74}
75
76#[allow(missing_docs)]
77/// Type details returned by Type::details() to inspect a type.
78pub enum TypeDetails<'a> {
79    Enum(TypeEnum<'a>),
80    Struct(TypeStruct<'a>),
81    Newtype(TypeNewtype<'a>),
82
83    Option(TypeId),
84    Vec(TypeId),
85    Map(TypeId, TypeId),
86    Set(TypeId),
87    Box(TypeId),
88    Tuple(Box<dyn Iterator<Item = TypeId> + 'a>),
89    Array(TypeId, usize),
90    Builtin(&'a str),
91
92    Unit,
93    String,
94}
95
96/// Enum type details.
97pub struct TypeEnum<'a> {
98    details: &'a type_entry::TypeEntryEnum,
99}
100
101/// Enum variant details.
102pub enum TypeEnumVariant<'a> {
103    /// Variant with no associated data.
104    Simple,
105    /// Tuple-type variant with at least one associated type.
106    Tuple(Vec<TypeId>),
107    /// Struct-type variant with named properties and types.
108    Struct(Vec<(&'a str, TypeId)>),
109}
110
111/// Full information pertaining to an enum variant.
112pub struct TypeEnumVariantInfo<'a> {
113    /// Name.
114    pub name: &'a str,
115    /// Description.
116    pub description: Option<&'a str>,
117    /// Details for the enum variant.
118    pub details: TypeEnumVariant<'a>,
119}
120
121/// Struct type details.
122pub struct TypeStruct<'a> {
123    details: &'a type_entry::TypeEntryStruct,
124}
125
126/// Full information pertaining to a struct property.
127pub struct TypeStructPropInfo<'a> {
128    /// Name.
129    pub name: &'a str,
130    /// Description.
131    pub description: Option<&'a str>,
132    /// Whether the propertty is required.
133    pub required: bool,
134    /// Identifies the schema for the property.
135    pub type_id: TypeId,
136}
137
138/// Newtype details.
139pub struct TypeNewtype<'a> {
140    details: &'a type_entry::TypeEntryNewtype,
141}
142
143/// Type identifier returned from type creation and used to lookup types.
144#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone, Hash)]
145pub struct TypeId(u64);
146
147#[derive(Debug, Clone, PartialEq)]
148pub(crate) enum Name {
149    Required(String),
150    Suggested(String),
151    Unknown,
152}
153
154impl Name {
155    pub fn into_option(self) -> Option<String> {
156        match self {
157            Name::Required(s) | Name::Suggested(s) => Some(s),
158            Name::Unknown => None,
159        }
160    }
161
162    pub fn append(&self, s: &str) -> Self {
163        match self {
164            Name::Required(prefix) | Name::Suggested(prefix) => {
165                Self::Suggested(format!("{}_{}", prefix, s))
166            }
167            Name::Unknown => Name::Unknown,
168        }
169    }
170}
171
172#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
173pub(crate) enum RefKey {
174    Root,
175    Def(String),
176}
177
178/// A collection of types.
179#[derive(Debug)]
180pub struct TypeSpace {
181    next_id: u64,
182
183    // TODO we need this in order to inspect the collection of reference types
184    // e.g. to do `all_mutually_exclusive`. In the future, we could obviate the
185    // need this by keeping a single Map of referenced types whose value was an
186    // enum of a "raw" or a "converted" schema.
187    definitions: BTreeMap<RefKey, Schema>,
188
189    id_to_entry: BTreeMap<TypeId, TypeEntry>,
190    type_to_id: BTreeMap<TypeEntryDetails, TypeId>,
191
192    name_to_id: BTreeMap<String, TypeId>,
193    ref_to_id: BTreeMap<RefKey, TypeId>,
194
195    uses_chrono: bool,
196    uses_uuid: bool,
197    uses_serde_json: bool,
198    uses_regress: bool,
199
200    settings: TypeSpaceSettings,
201
202    cache: SchemaCache,
203
204    // Shared functions for generating default values
205    defaults: BTreeSet<DefaultImpl>,
206}
207
208impl Default for TypeSpace {
209    fn default() -> Self {
210        Self {
211            next_id: 1,
212            definitions: Default::default(),
213            id_to_entry: Default::default(),
214            type_to_id: Default::default(),
215            name_to_id: Default::default(),
216            ref_to_id: Default::default(),
217            uses_chrono: Default::default(),
218            uses_uuid: Default::default(),
219            uses_serde_json: Default::default(),
220            uses_regress: Default::default(),
221            settings: Default::default(),
222            cache: Default::default(),
223            defaults: Default::default(),
224        }
225    }
226}
227
228#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
229pub(crate) enum DefaultImpl {
230    Boolean,
231    I64,
232    U64,
233    NZU64,
234}
235
236/// Type name to use in generated code.
237#[derive(Clone)]
238pub struct MapType(pub syn::Type);
239
240impl MapType {
241    /// Create a new MapType from a [`str`].
242    pub fn new(s: &str) -> Self {
243        let map_type = syn::parse_str::<syn::Type>(s).expect("valid ident");
244        Self(map_type)
245    }
246}
247
248impl Default for MapType {
249    fn default() -> Self {
250        Self::new("::std::collections::HashMap")
251    }
252}
253
254impl std::fmt::Debug for MapType {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        write!(f, "MapType({})", self.0.to_token_stream())
257    }
258}
259
260impl std::fmt::Display for MapType {
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        self.0.to_token_stream().fmt(f)
263    }
264}
265
266impl<'de> serde::Deserialize<'de> for MapType {
267    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
268    where
269        D: serde::Deserializer<'de>,
270    {
271        let s = <&str>::deserialize(deserializer)?;
272        Ok(Self::new(s))
273    }
274}
275
276impl From<String> for MapType {
277    fn from(s: String) -> Self {
278        Self::new(&s)
279    }
280}
281
282impl From<&str> for MapType {
283    fn from(s: &str) -> Self {
284        Self::new(s)
285    }
286}
287
288impl From<syn::Type> for MapType {
289    fn from(t: syn::Type) -> Self {
290        Self(t)
291    }
292}
293
294/// Settings that alter type generation.
295#[derive(Default, Debug, Clone)]
296pub struct TypeSpaceSettings {
297    type_mod: Option<String>,
298    extra_derives: Vec<String>,
299    extra_attrs: Vec<String>,
300    struct_builder: bool,
301
302    unknown_crates: UnknownPolicy,
303    crates: BTreeMap<String, CrateSpec>,
304    map_type: MapType,
305
306    patch: BTreeMap<String, TypeSpacePatch>,
307    replace: BTreeMap<String, TypeSpaceReplace>,
308    convert: Vec<TypeSpaceConversion>,
309}
310
311#[derive(Debug, Clone)]
312struct CrateSpec {
313    version: CrateVers,
314    rename: Option<String>,
315}
316
317/// Policy to apply to external types described by schema extensions whose
318/// crates are not explicitly specified.
319#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, serde::Deserialize)]
320pub enum UnknownPolicy {
321    /// Generate the type rather according to the schema.
322    #[default]
323    Generate,
324    /// Use the specified type by path (this will result in a compile error if
325    /// one of the crates is not an existing dependency). Note that this
326    /// ignores compatibility requirements specified by the schema extension
327    /// and may result in subtle failures if the crate used is incompatible
328    /// with the version that produced the schema.
329    Allow,
330    /// If an unknown crate is encountered, generate a compiler warning
331    /// indicating the crate that must be specified to proceed along with
332    /// version constraints. This affords users an opportunity to specify the
333    /// specific crate version to use (or the user may explicitly deny use of
334    /// that crate).
335    Deny,
336}
337
338/// Specify the version for a named crate to consider for type use (rather than
339/// generating types) in the presense of a schema extension.
340#[derive(Debug, Clone)]
341pub enum CrateVers {
342    /// An explicit version.
343    Version(semver::Version),
344    /// Any version.
345    Any,
346    /// Never use the given crate.
347    Never,
348}
349
350impl CrateVers {
351    /// Parse from a string
352    pub fn parse(s: &str) -> Option<Self> {
353        if s == "!" {
354            Some(Self::Never)
355        } else if s == "*" {
356            Some(Self::Any)
357        } else {
358            Some(Self::Version(semver::Version::parse(s).ok()?))
359        }
360    }
361}
362
363/// Contains a set of modifications that may be applied to an existing type.
364#[derive(Debug, Default, Clone)]
365pub struct TypeSpacePatch {
366    rename: Option<String>,
367    derives: Vec<String>,
368    attrs: Vec<String>,
369}
370
371/// Contains the attributes of a replacement of an existing type.
372#[derive(Debug, Default, Clone)]
373pub struct TypeSpaceReplace {
374    replace_type: String,
375    impls: Vec<TypeSpaceImpl>,
376}
377
378/// Defines a schema which will be replaced, and the attributes of the
379/// replacement.
380#[derive(Debug, Clone)]
381struct TypeSpaceConversion {
382    schema: schemars::schema::SchemaObject,
383    type_name: String,
384    impls: Vec<TypeSpaceImpl>,
385}
386
387#[allow(missing_docs)]
388// TODO we can currently only address traits for which cycle analysis is not
389// required.
390#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
391#[non_exhaustive]
392pub enum TypeSpaceImpl {
393    FromStr,
394    FromStringIrrefutable,
395    Display,
396    Default,
397}
398
399impl std::str::FromStr for TypeSpaceImpl {
400    type Err = String;
401
402    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
403        match s {
404            "FromStr" => Ok(Self::FromStr),
405            "Display" => Ok(Self::Display),
406            "Default" => Ok(Self::Default),
407            _ => Err(format!("{} is not a valid trait specifier", s)),
408        }
409    }
410}
411
412impl TypeSpaceSettings {
413    /// Set the name of the path prefix for types defined in this [TypeSpace].
414    pub fn with_type_mod<S: AsRef<str>>(&mut self, type_mod: S) -> &mut Self {
415        self.type_mod = Some(type_mod.as_ref().to_string());
416        self
417    }
418
419    /// Add an additional derive macro to apply to all defined types.
420    pub fn with_derive(&mut self, derive: String) -> &mut Self {
421        if !self.extra_derives.contains(&derive) {
422            self.extra_derives.push(derive);
423        }
424        self
425    }
426
427    /// Add an additional attribute to apply to all defined types.
428    pub fn with_attr(&mut self, attr: String) -> &mut Self {
429        if !self.extra_attrs.contains(&attr) {
430            self.extra_attrs.push(attr);
431        }
432        self
433    }
434
435    /// For structs, include a "builder" type that can be used to construct it.
436    pub fn with_struct_builder(&mut self, struct_builder: bool) -> &mut Self {
437        self.struct_builder = struct_builder;
438        self
439    }
440
441    /// Replace a referenced type with a named type. This causes the referenced
442    /// type *not* to be generated. If the same `type_name` is specified multiple times,
443    /// the last one is honored.
444    pub fn with_replacement<TS: ToString, RS: ToString, I: Iterator<Item = TypeSpaceImpl>>(
445        &mut self,
446        type_name: TS,
447        replace_type: RS,
448        impls: I,
449    ) -> &mut Self {
450        self.replace.insert(
451            type_name.to_string(),
452            TypeSpaceReplace {
453                replace_type: replace_type.to_string(),
454                impls: impls.collect(),
455            },
456        );
457        self
458    }
459
460    /// Modify a type with the given name. Note that specifying a type not
461    /// created by the input JSON schema does **not** result in an error and is
462    /// silently ignored. If the same `type_name` is specified multiple times,
463    /// the last one is honored.
464    pub fn with_patch<S: ToString>(
465        &mut self,
466        type_name: S,
467        type_patch: &TypeSpacePatch,
468    ) -> &mut Self {
469        self.patch.insert(type_name.to_string(), type_patch.clone());
470        self
471    }
472
473    /// Replace a given schema with a named type. The given schema must precisely
474    /// match the schema from the input, including fields such as `description`.
475    /// Typical usage is to map a schema definition to a builtin type or type
476    /// provided by a crate, such as `'rust_decimal::Decimal'`. If the same schema
477    /// is specified multiple times, the first one is honored.
478    ///
479    /// # Examples
480    ///
481    /// ```
482    /// // Setup 'number' json type to be translated into 'rust_decimal::Decimal'
483    /// use schemars::schema::{InstanceType, SchemaObject};
484    /// use typify_impl::{TypeSpace, TypeSpaceImpl, TypeSpaceSettings};
485    /// let mut type_space = TypeSpace::new(
486    ///        TypeSpaceSettings::default()
487    ///            .with_struct_builder(true)
488    ///            .with_conversion(
489    ///                SchemaObject {
490    ///                    instance_type: Some(InstanceType::Number.into()),
491    ///                    ..Default::default()
492    ///                },
493    ///                "::rust_decimal::Decimal",
494    ///                [TypeSpaceImpl::Display].into_iter(),
495    ///            ),
496    ///    );
497    /// ```
498    pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
499        &mut self,
500        schema: schemars::schema::SchemaObject,
501        type_name: S,
502        impls: I,
503    ) -> &mut Self {
504        self.convert.push(TypeSpaceConversion {
505            schema,
506            type_name: type_name.to_string(),
507            impls: impls.collect(),
508        });
509        self
510    }
511
512    /// Type schemas may contain an extension (`x-rust-type`) that indicates
513    /// the corresponding Rust type within a particular crate. This function
514    /// changes the disposition regarding crates not otherwise specified via
515    /// [`Self::with_crate`]. The default value is `false`.
516    pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
517        self.unknown_crates = policy;
518        self
519    }
520
521    /// Type schemas may contain an extension (`x-rust-type`) that indicates
522    /// the corresponding Rust type within a particular crate. This extension
523    /// indicates the crate, version compatibility, type path, and type
524    /// parameters. This function modifies settings to use (rather than
525    /// generate) types from the given crate and version. The version should
526    /// precisely match the version of the crate that you expect as a
527    /// dependency.
528    pub fn with_crate<S1: ToString>(
529        &mut self,
530        crate_name: S1,
531        version: CrateVers,
532        rename: Option<&String>,
533    ) -> &mut Self {
534        self.crates.insert(
535            crate_name.to_string(),
536            CrateSpec {
537                version,
538                rename: rename.cloned(),
539            },
540        );
541        self
542    }
543
544    /// Specify the map-like type to be used in generated code.
545    ///
546    /// ## Requirements
547    ///
548    /// - An `is_empty` method that returns a boolean
549    /// - Two generic parameters, `K` and `V`
550    /// - [`Default`] + [`Clone`] + [`Debug`] +
551    ///   [`Serialize`][serde::Serialize] + [`Deserialize`][serde::Deserialize]
552    ///
553    /// ## Examples
554    ///
555    /// - [`::std::collections::HashMap`]
556    /// - [`::std::collections::BTreeMap`]
557    /// - [`::indexmap::IndexMap`](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html)
558    pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
559        self.map_type = map_type.into();
560        self
561    }
562}
563
564impl TypeSpacePatch {
565    /// Specify the new name for patched type.
566    pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
567        self.rename = Some(rename.to_string());
568        self
569    }
570
571    /// Specify an additional derive to apply to the patched type.
572    pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
573        self.derives.push(derive.to_string());
574        self
575    }
576
577    /// Specify an additional attribute to apply to the patched type.
578    pub fn with_attr<S: ToString>(&mut self, attr: S) -> &mut Self {
579        self.attrs.push(attr.to_string());
580        self
581    }
582}
583
584impl TypeSpace {
585    /// Create a new TypeSpace with custom settings.
586    pub fn new(settings: &TypeSpaceSettings) -> Self {
587        let mut cache = SchemaCache::default();
588
589        settings.convert.iter().for_each(
590            |TypeSpaceConversion {
591                 schema,
592                 type_name,
593                 impls,
594             }| {
595                cache.insert(schema, type_name, impls);
596            },
597        );
598
599        Self {
600            settings: settings.clone(),
601            cache,
602            ..Default::default()
603        }
604    }
605
606    /// Add a collection of types that will be used as references. Regardless
607    /// of how these types are defined--*de novo* or built-in--each type will
608    /// appear in the final output as a struct, enum or newtype. This method
609    /// may be called multiple times, but collections of references must be
610    /// self-contained; in other words, a type in one invocation may not refer
611    /// to a type in another invocation.
612    // TODO on an error the TypeSpace is in a weird state; we, perhaps, create
613    // a child TypeSpace and then merge it in once all conversions hae
614    // succeeded.
615    pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
616    where
617        I: IntoIterator<Item = (S, Schema)>,
618        S: AsRef<str>,
619    {
620        self.add_ref_types_impl(
621            type_defs
622                .into_iter()
623                .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
624        )
625    }
626
627    fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
628    where
629        I: IntoIterator<Item = (RefKey, Schema)>,
630    {
631        // Gather up all types to make things a little more convenient.
632        let definitions = type_defs.into_iter().collect::<Vec<_>>();
633
634        // Assign IDs to reference types before actually converting them. We'll
635        // need these in the case of forward (or circular) references.
636        let base_id = self.next_id;
637        let def_len = definitions.len() as u64;
638        self.next_id += def_len;
639
640        for (index, (ref_name, schema)) in definitions.iter().enumerate() {
641            self.ref_to_id
642                .insert(ref_name.clone(), TypeId(base_id + index as u64));
643            self.definitions.insert(ref_name.clone(), schema.clone());
644        }
645
646        // Convert all types; note that we use the type id assigned from the
647        // previous step because each type may create additional types. This
648        // effectively is doing the work of `add_type_with_name` but for a
649        // batch of types.
650        for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
651            info!(
652                "converting type: {:?} with schema {}",
653                ref_name,
654                serde_json::to_string(&schema).unwrap()
655            );
656
657            // Check for manually replaced types. Proceed with type conversion
658            // if there is none; use the specified type if there is.
659            let type_id = TypeId(base_id + index as u64);
660
661            let maybe_replace = match &ref_name {
662                RefKey::Root => None,
663                RefKey::Def(def_name) => {
664                    let check_name = sanitize(def_name, Case::Pascal);
665                    self.settings.replace.get(&check_name)
666                }
667            };
668
669            match maybe_replace {
670                None => {
671                    let type_name = if let RefKey::Def(name) = ref_name {
672                        Name::Required(name.clone())
673                    } else {
674                        Name::Unknown
675                    };
676                    self.convert_ref_type(type_name, schema, type_id)?
677                }
678
679                Some(replace_type) => {
680                    let type_entry = TypeEntry::new_native(
681                        replace_type.replace_type.clone(),
682                        &replace_type.impls.clone(),
683                    );
684                    self.id_to_entry.insert(type_id, type_entry);
685                }
686            }
687        }
688
689        // Eliminate cycles. It's sufficient to only start from referenced
690        // types as a reference is required to make a cycle.
691        self.break_cycles(base_id..base_id + def_len);
692
693        // Finalize all created types.
694        for index in base_id..self.next_id {
695            let type_id = TypeId(index);
696            let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
697            type_entry.finalize(self)?;
698            self.id_to_entry.insert(type_id, type_entry);
699        }
700
701        Ok(())
702    }
703
704    fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
705        let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
706        let default = metadata
707            .as_ref()
708            .and_then(|m| m.default.as_ref())
709            .cloned()
710            .map(WrappedValue::new);
711        let type_entry = match &mut type_entry.details {
712            // The types that are already named are good to go.
713            TypeEntryDetails::Enum(details) => {
714                details.default = default;
715                type_entry
716            }
717            TypeEntryDetails::Struct(details) => {
718                details.default = default;
719                type_entry
720            }
721            TypeEntryDetails::Newtype(details) => {
722                details.default = default;
723                type_entry
724            }
725
726            // If the type entry is a reference, then this definition is a
727            // simple alias to another type in this list of definitions
728            // (which may nor may not have already been converted). We
729            // simply create a newtype with that type ID.
730            TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
731                self,
732                type_name,
733                metadata,
734                type_id.clone(),
735                schema.clone(),
736            ),
737
738            TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
739
740            // For types that don't have names, this is effectively a type
741            // alias which we treat as a newtype.
742            _ => {
743                info!(
744                    "type alias {:?} {}\n{:?}",
745                    type_name,
746                    serde_json::to_string_pretty(&schema).unwrap(),
747                    metadata
748                );
749                let subtype_id = self.assign_type(type_entry);
750                TypeEntryNewtype::from_metadata(
751                    self,
752                    type_name,
753                    metadata,
754                    subtype_id,
755                    schema.clone(),
756                )
757            }
758        };
759        // TODO need a type alias?
760        if let Some(entry_name) = type_entry.name() {
761            self.name_to_id.insert(entry_name.clone(), type_id.clone());
762        }
763        self.id_to_entry.insert(type_id, type_entry);
764        Ok(())
765    }
766
767    /// Add a new type and return a type identifier that may be used in
768    /// function signatures or embedded within other types.
769    pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
770        self.add_type_with_name(schema, None)
771    }
772
773    /// Add a new type with a name hint and return a the components necessary
774    /// to use the type for various components of a function signature.
775    pub fn add_type_with_name(
776        &mut self,
777        schema: &Schema,
778        name_hint: Option<String>,
779    ) -> Result<TypeId> {
780        let base_id = self.next_id;
781
782        let name = match name_hint {
783            Some(s) => Name::Suggested(s),
784            None => Name::Unknown,
785        };
786        let (type_id, _) = self.id_for_schema(name, schema)?;
787
788        // Finalize all created types.
789        for index in base_id..self.next_id {
790            let type_id = TypeId(index);
791            let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
792            type_entry.finalize(self)?;
793            self.id_to_entry.insert(type_id, type_entry);
794        }
795
796        Ok(type_id)
797    }
798
799    /// Add all the types contained within a RootSchema including any
800    /// referenced types and the top-level type (if there is one and it has a
801    /// title).
802    pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
803        let RootSchema {
804            meta_schema: _,
805            schema,
806            definitions,
807        } = schema;
808
809        let mut defs = definitions
810            .into_iter()
811            .map(|(key, schema)| (RefKey::Def(key), schema))
812            .collect::<Vec<_>>();
813
814        // Does the root type have a name (otherwise... ignore it)
815        let root_type = schema
816            .metadata
817            .as_ref()
818            .and_then(|m| m.title.as_ref())
819            .is_some();
820
821        if root_type {
822            defs.push((RefKey::Root, schema.into()));
823        }
824
825        self.add_ref_types_impl(defs)?;
826
827        if root_type {
828            Ok(self.ref_to_id.get(&RefKey::Root).cloned())
829        } else {
830            Ok(None)
831        }
832    }
833
834    /// Get a type given its ID.
835    pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
836        let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
837        Ok(Type {
838            type_space: self,
839            type_entry,
840        })
841    }
842
843    /// Whether the generated code needs `chrono` crate.
844    pub fn uses_chrono(&self) -> bool {
845        self.uses_chrono
846    }
847
848    /// Whether the generated code needs [regress] crate.
849    pub fn uses_regress(&self) -> bool {
850        self.uses_regress
851    }
852
853    /// Whether the generated code needs [serde_json] crate.
854    pub fn uses_serde_json(&self) -> bool {
855        self.uses_serde_json
856    }
857
858    /// Whether the generated code needs `uuid` crate.
859    pub fn uses_uuid(&self) -> bool {
860        self.uses_uuid
861    }
862
863    /// Iterate over all types including those defined in this [TypeSpace] and
864    /// those referred to by those types.
865    pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
866        self.id_to_entry.values().map(move |type_entry| Type {
867            type_space: self,
868            type_entry,
869        })
870    }
871
872    /// All code for processed types.
873    pub fn to_stream(&self) -> TokenStream {
874        let mut output = OutputSpace::default();
875
876        // Add the error type we use for conversions; it's fine if this is
877        // unused.
878        output.add_item(
879            output::OutputSpaceMod::Error,
880            "",
881            quote! {
882                /// Error from a `TryFrom` or `FromStr` implementation.
883                pub struct ConversionError(::std::borrow::Cow<'static, str>);
884
885                impl ::std::error::Error for ConversionError {}
886                impl ::std::fmt::Display for ConversionError {
887                    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
888                        -> Result<(), ::std::fmt::Error>
889                    {
890                        ::std::fmt::Display::fmt(&self.0, f)
891                    }
892                }
893
894                impl ::std::fmt::Debug for ConversionError {
895                    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
896                        -> Result<(), ::std::fmt::Error>
897                    {
898                        ::std::fmt::Debug::fmt(&self.0, f)
899                    }
900                }
901                impl From<&'static str> for ConversionError {
902                    fn from(value: &'static str) -> Self {
903                        Self(value.into())
904                    }
905                }
906                impl From<String> for ConversionError {
907                    fn from(value: String) -> Self {
908                        Self(value.into())
909                    }
910                }
911            },
912        );
913
914        // Add all types.
915        self.id_to_entry
916            .values()
917            .for_each(|type_entry| type_entry.output(self, &mut output));
918
919        // Add all shared default functions.
920        self.defaults
921            .iter()
922            .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
923
924        output.into_stream()
925    }
926
927    /// Allocated the next TypeId.
928    fn assign(&mut self) -> TypeId {
929        let id = TypeId(self.next_id);
930        self.next_id += 1;
931        id
932    }
933
934    /// Assign a TypeId for a TypeEntry. This handles resolving references,
935    /// checking for duplicate type definitions (e.g. to make sure there aren't
936    /// two conflicting types of the same name), and deduplicates various
937    /// flavors of built-in types.
938    fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
939        if let TypeEntryDetails::Reference(type_id) = ty.details {
940            type_id
941        } else if let Some(name) = ty.name() {
942            // If there's already a type of this name, we make sure it's
943            // identical. Note that this covers all user-defined types.
944
945            // TODO there are many different choices we might make here
946            // that could differ depending on the texture of the schema.
947            // For example, a schema might use the string "Response" in a
948            // bunch of places and if that were the case we might expect
949            // them to be different and resolve that by renaming or scoping
950            // them in some way.
951            if let Some(type_id) = self.name_to_id.get(name) {
952                // TODO we'd like to verify that the type is structurally the
953                // same, but the types may not be functionally equal. This is a
954                // consequence of types being "finalized" after each type
955                // addition. This further emphasized the need for a more
956                // deliberate, multi-pass approach.
957                type_id.clone()
958            } else {
959                let type_id = self.assign();
960                self.name_to_id.insert(name.clone(), type_id.clone());
961                self.id_to_entry.insert(type_id.clone(), ty);
962                type_id
963            }
964        } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
965            type_id.clone()
966        } else {
967            let type_id = self.assign();
968            self.type_to_id.insert(ty.details.clone(), type_id.clone());
969            self.id_to_entry.insert(type_id.clone(), ty);
970            type_id
971        }
972    }
973
974    /// Convert a schema to a TypeEntry and assign it a TypeId.
975    ///
976    /// This is used for sub-types such as the type of an array or the types of
977    /// properties of a struct.
978    fn id_for_schema<'a>(
979        &mut self,
980        type_name: Name,
981        schema: &'a Schema,
982    ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
983        let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
984        if let Some(metadata) = metadata {
985            let default = metadata.default.clone().map(WrappedValue::new);
986            match &mut type_entry.details {
987                TypeEntryDetails::Enum(details) => {
988                    details.default = default;
989                }
990                TypeEntryDetails::Struct(details) => {
991                    details.default = default;
992                }
993                TypeEntryDetails::Newtype(details) => {
994                    details.default = default;
995                }
996                _ => (),
997            }
998        }
999        let type_id = self.assign_type(type_entry);
1000        Ok((type_id, metadata))
1001    }
1002
1003    /// Create an Option<T> from a pre-assigned TypeId and assign it an ID.
1004    fn id_to_option(&mut self, id: &TypeId) -> TypeId {
1005        self.assign_type(TypeEntryDetails::Option(id.clone()).into())
1006    }
1007
1008    // Create an Option<T> from a TypeEntry by assigning it type.
1009    fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
1010        TypeEntryDetails::Option(self.assign_type(ty)).into()
1011    }
1012
1013    /// Create a Box<T> from a pre-assigned TypeId and assign it an ID.
1014    fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1015        self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1016    }
1017}
1018
1019impl ToTokens for TypeSpace {
1020    fn to_tokens(&self, tokens: &mut TokenStream) {
1021        tokens.extend(self.to_stream())
1022    }
1023}
1024
1025impl Type<'_> {
1026    /// The name of the type as a String.
1027    pub fn name(&self) -> String {
1028        let Type {
1029            type_space,
1030            type_entry,
1031        } = self;
1032        type_entry.type_name(type_space)
1033    }
1034
1035    /// The identifier for the type as might be used for a function return or
1036    /// defining the type of a member of a struct..
1037    pub fn ident(&self) -> TokenStream {
1038        let Type {
1039            type_space,
1040            type_entry,
1041        } = self;
1042        type_entry.type_ident(type_space, &type_space.settings.type_mod)
1043    }
1044
1045    /// The identifier for the type as might be used for a parameter in a
1046    /// function signature. In general: simple types are the same as
1047    /// [Type::ident] and complex types prepend a `&`.
1048    pub fn parameter_ident(&self) -> TokenStream {
1049        let Type {
1050            type_space,
1051            type_entry,
1052        } = self;
1053        type_entry.type_parameter_ident(type_space, None)
1054    }
1055
1056    /// The identifier for the type as might be used for a parameter in a
1057    /// function signature along with a lifetime parameter. In general: simple
1058    /// types are the same as [Type::ident] and complex types prepend a
1059    /// `&'<lifetime>`.
1060    pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1061        let Type {
1062            type_space,
1063            type_entry,
1064        } = self;
1065        type_entry.type_parameter_ident(type_space, Some(lifetime))
1066    }
1067
1068    /// A textual description of the type appropriate for debug output.
1069    pub fn describe(&self) -> String {
1070        self.type_entry.describe()
1071    }
1072
1073    /// Get details about the type.
1074    pub fn details(&self) -> TypeDetails<'_> {
1075        match &self.type_entry.details {
1076            // Named user-defined types
1077            TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1078            TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1079            TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1080
1081            // Compound types
1082            TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1083            TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1084            TypeEntryDetails::Map(key_id, value_id) => {
1085                TypeDetails::Map(key_id.clone(), value_id.clone())
1086            }
1087            TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1088            TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1089            TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1090            TypeEntryDetails::Array(type_id, length) => {
1091                TypeDetails::Array(type_id.clone(), *length)
1092            }
1093
1094            // Builtin types
1095            TypeEntryDetails::Unit => TypeDetails::Unit,
1096            TypeEntryDetails::Native(TypeEntryNative {
1097                type_name: name, ..
1098            })
1099            | TypeEntryDetails::Integer(name)
1100            | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1101            TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1102            TypeEntryDetails::String => TypeDetails::String,
1103            TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1104
1105            // Only used during processing; shouldn't be visible at this point
1106            TypeEntryDetails::Reference(_) => unreachable!(),
1107        }
1108    }
1109
1110    /// Checks if the type has the associated impl.
1111    pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1112        let Type {
1113            type_space,
1114            type_entry,
1115        } = self;
1116        type_entry.has_impl(type_space, impl_name)
1117    }
1118
1119    /// Provides the the type identifier for the builder if one exists.
1120    pub fn builder(&self) -> Option<TokenStream> {
1121        let Type {
1122            type_space,
1123            type_entry,
1124        } = self;
1125
1126        if !type_space.settings.struct_builder {
1127            return None;
1128        }
1129
1130        match &type_entry.details {
1131            TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1132                match &type_space.settings.type_mod {
1133                    Some(type_mod) => {
1134                        let type_mod = format_ident!("{}", type_mod);
1135                        let type_name = format_ident!("{}", name);
1136                        Some(quote! { #type_mod :: builder :: #type_name })
1137                    }
1138                    None => {
1139                        let type_name = format_ident!("{}", name);
1140                        Some(quote! { builder :: #type_name })
1141                    }
1142                }
1143            }
1144            _ => None,
1145        }
1146    }
1147}
1148
1149impl<'a> TypeEnum<'a> {
1150    /// Get name and information of each enum variant.
1151    pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1152        self.variants_info().map(|info| (info.name, info.details))
1153    }
1154
1155    /// Get all information for each enum variant.
1156    pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1157        self.details.variants.iter().map(move |variant| {
1158            let details = match &variant.details {
1159                type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1160                // The distinction between a lone item variant and a tuple
1161                // variant with a single item is only relevant internally.
1162                type_entry::VariantDetails::Item(type_id) => {
1163                    TypeEnumVariant::Tuple(vec![type_id.clone()])
1164                }
1165                type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1166                type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1167                    properties
1168                        .iter()
1169                        .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1170                        .collect(),
1171                ),
1172            };
1173            TypeEnumVariantInfo {
1174                name: variant.ident_name.as_ref().unwrap(),
1175                description: variant.description.as_deref(),
1176                details,
1177            }
1178        })
1179    }
1180}
1181
1182impl<'a> TypeStruct<'a> {
1183    /// Get name and type of each property.
1184    pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1185        self.details
1186            .properties
1187            .iter()
1188            .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1189    }
1190
1191    /// Get all information about each struct property.
1192    pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1193        self.details
1194            .properties
1195            .iter()
1196            .map(move |prop| TypeStructPropInfo {
1197                name: prop.name.as_str(),
1198                description: prop.description.as_deref(),
1199                required: matches!(&prop.state, StructPropertyState::Required),
1200                type_id: prop.type_id.clone(),
1201            })
1202    }
1203}
1204
1205impl TypeNewtype<'_> {
1206    /// Get the inner type of the newtype struct.
1207    pub fn inner(&self) -> TypeId {
1208        self.details.type_id.clone()
1209    }
1210}
1211
1212#[cfg(test)]
1213mod tests {
1214    use schema::Schema;
1215    use schemars::{schema_for, JsonSchema};
1216    use serde::Serialize;
1217    use serde_json::json;
1218    use std::collections::HashSet;
1219
1220    use crate::{
1221        output::OutputSpace,
1222        test_util::validate_output,
1223        type_entry::{TypeEntryEnum, VariantDetails},
1224        Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1225    };
1226
1227    #[allow(dead_code)]
1228    #[derive(Serialize, JsonSchema)]
1229    struct Blah {
1230        blah: String,
1231    }
1232
1233    #[allow(dead_code)]
1234    #[derive(Serialize, JsonSchema)]
1235    #[serde(rename_all = "camelCase")]
1236    //#[serde(untagged)]
1237    //#[serde(tag = "type", content = "content")]
1238    enum E {
1239        /// aaa
1240        A,
1241        /// bee
1242        B,
1243        /// cee
1244        //C(Vec<String>),
1245        C(Blah),
1246        /// dee
1247        D {
1248            /// double D
1249            dd: String,
1250        },
1251        // /// eff
1252        // F(
1253        //     /// eff.0
1254        //     u32,
1255        //     /// eff.1
1256        //     u32,
1257        // ),
1258    }
1259
1260    #[allow(dead_code)]
1261    #[derive(JsonSchema)]
1262    #[serde(rename_all = "camelCase")]
1263    struct Foo {
1264        /// this is bar
1265        #[serde(default)]
1266        bar: Option<String>,
1267        baz_baz: i32,
1268        /// eeeeee!
1269        e: E,
1270    }
1271
1272    #[test]
1273    fn test_simple() {
1274        let schema = schema_for!(Foo);
1275        println!("{:#?}", schema);
1276        let mut type_space = TypeSpace::default();
1277        type_space.add_ref_types(schema.definitions).unwrap();
1278        let (ty, _) = type_space
1279            .convert_schema_object(
1280                Name::Unknown,
1281                &schemars::schema::Schema::Object(schema.schema.clone()),
1282                &schema.schema,
1283            )
1284            .unwrap();
1285
1286        println!("{:#?}", ty);
1287
1288        let mut output = OutputSpace::default();
1289        ty.output(&type_space, &mut output);
1290        println!("{}", output.into_stream());
1291
1292        for ty in type_space.id_to_entry.values() {
1293            println!("{:#?}", ty);
1294            let mut output = OutputSpace::default();
1295            ty.output(&type_space, &mut output);
1296            println!("{}", output.into_stream());
1297        }
1298    }
1299
1300    #[test]
1301    fn test_external_references() {
1302        let schema = json!({
1303            "$schema": "http://json-schema.org/draft-04/schema#",
1304            "definitions": {
1305                "somename": {
1306                    "$ref": "#/definitions/someothername",
1307                    "required": [ "someproperty" ]
1308                },
1309                "someothername": {
1310                    "type": "object",
1311                    "properties": {
1312                        "someproperty": {
1313                            "type": "string"
1314                        }
1315                    }
1316                }
1317            }
1318        });
1319        let schema = serde_json::from_value(schema).unwrap();
1320        println!("{:#?}", schema);
1321        let settings = TypeSpaceSettings::default();
1322        let mut type_space = TypeSpace::new(&settings);
1323        type_space.add_root_schema(schema).unwrap();
1324        let tokens = type_space.to_stream().to_string();
1325        println!("{}", tokens);
1326        assert!(tokens
1327            .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1328    }
1329
1330    #[test]
1331    fn test_convert_enum_string() {
1332        #[allow(dead_code)]
1333        #[derive(JsonSchema)]
1334        #[serde(rename_all = "camelCase")]
1335        enum SimpleEnum {
1336            DotCom,
1337            Grizz,
1338            Kenneth,
1339        }
1340
1341        let schema = schema_for!(SimpleEnum);
1342        println!("{:#?}", schema);
1343
1344        let mut type_space = TypeSpace::default();
1345        type_space.add_ref_types(schema.definitions).unwrap();
1346        let (ty, _) = type_space
1347            .convert_schema_object(
1348                Name::Unknown,
1349                &schemars::schema::Schema::Object(schema.schema.clone()),
1350                &schema.schema,
1351            )
1352            .unwrap();
1353
1354        match ty.details {
1355            TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1356                for variant in &variants {
1357                    assert_eq!(variant.details, VariantDetails::Simple);
1358                }
1359                let var_names = variants
1360                    .iter()
1361                    .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1362                    .collect::<HashSet<_>>();
1363                assert_eq!(
1364                    var_names,
1365                    ["DotCom", "Grizz", "Kenneth",]
1366                        .iter()
1367                        .map(ToString::to_string)
1368                        .collect::<HashSet<_>>()
1369                );
1370            }
1371            _ => {
1372                let mut output = OutputSpace::default();
1373                ty.output(&type_space, &mut output);
1374                println!("{}", output.into_stream());
1375                panic!();
1376            }
1377        }
1378    }
1379
1380    #[test]
1381    fn test_string_enum_with_null() {
1382        let original_schema = json!({ "$ref": "xxx"});
1383        let enum_values = vec![
1384            json!("Shadrach"),
1385            json!("Meshach"),
1386            json!("Abednego"),
1387            json!(null),
1388        ];
1389
1390        let mut type_space = TypeSpace::default();
1391        let (te, _) = type_space
1392            .convert_enum_string(
1393                Name::Required("OnTheGo".to_string()),
1394                &serde_json::from_value(original_schema).unwrap(),
1395                &None,
1396                &enum_values,
1397                None,
1398            )
1399            .unwrap();
1400
1401        if let TypeEntryDetails::Option(id) = &te.details {
1402            let ote = type_space.id_to_entry.get(id).unwrap();
1403            if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1404                let variants = variants
1405                    .iter()
1406                    .map(|v| match v.details {
1407                        VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1408                        _ => panic!("unexpected variant type"),
1409                    })
1410                    .collect::<HashSet<_>>();
1411
1412                assert_eq!(
1413                    variants,
1414                    enum_values
1415                        .iter()
1416                        .flat_map(|j| j.as_str().map(ToString::to_string))
1417                        .collect::<HashSet<_>>()
1418                );
1419            } else {
1420                panic!("not the sub-type we expected {:#?}", te)
1421            }
1422        } else {
1423            panic!("not the type we expected {:#?}", te)
1424        }
1425    }
1426
1427    #[test]
1428    fn test_alias() {
1429        #[allow(dead_code)]
1430        #[derive(JsonSchema, Schema)]
1431        struct Stuff(Vec<String>);
1432
1433        #[allow(dead_code)]
1434        #[derive(JsonSchema, Schema)]
1435        struct Things {
1436            a: String,
1437            b: Stuff,
1438        }
1439
1440        validate_output::<Things>();
1441    }
1442
1443    #[test]
1444    fn test_builder_name() {
1445        #[allow(dead_code)]
1446        #[derive(JsonSchema)]
1447        struct TestStruct {
1448            x: u32,
1449        }
1450
1451        let mut type_space = TypeSpace::default();
1452        let schema = schema_for!(TestStruct);
1453        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1454        let ty = type_space.get_type(&type_id).unwrap();
1455
1456        assert!(ty.builder().is_none());
1457
1458        let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1459        let schema = schema_for!(TestStruct);
1460        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1461        let ty = type_space.get_type(&type_id).unwrap();
1462
1463        assert_eq!(
1464            ty.builder().map(|ts| ts.to_string()),
1465            Some("builder :: TestStruct".to_string())
1466        );
1467
1468        let mut type_space = TypeSpace::new(
1469            TypeSpaceSettings::default()
1470                .with_type_mod("types")
1471                .with_struct_builder(true),
1472        );
1473        let schema = schema_for!(TestStruct);
1474        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1475        let ty = type_space.get_type(&type_id).unwrap();
1476
1477        assert_eq!(
1478            ty.builder().map(|ts| ts.to_string()),
1479            Some("types :: builder :: TestStruct".to_string())
1480        );
1481
1482        #[allow(dead_code)]
1483        #[derive(JsonSchema)]
1484        enum TestEnum {
1485            X,
1486            Y,
1487        }
1488        let mut type_space = TypeSpace::new(
1489            TypeSpaceSettings::default()
1490                .with_type_mod("types")
1491                .with_struct_builder(true),
1492        );
1493        let schema = schema_for!(TestEnum);
1494        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1495        let ty = type_space.get_type(&type_id).unwrap();
1496        assert!(ty.builder().is_none());
1497    }
1498}