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