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
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            type_entry.finalize(self)?;
700            self.id_to_entry.insert(type_id, type_entry);
701        }
702
703        Ok(())
704    }
705
706    fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
707        let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
708        let default = metadata
709            .as_ref()
710            .and_then(|m| m.default.as_ref())
711            .cloned()
712            .map(WrappedValue::new);
713        let type_entry = match &mut type_entry.details {
714            // The types that are already named are good to go.
715            TypeEntryDetails::Enum(details) => {
716                details.default = default;
717                type_entry
718            }
719            TypeEntryDetails::Struct(details) => {
720                details.default = default;
721                type_entry
722            }
723            TypeEntryDetails::Newtype(details) => {
724                details.default = default;
725                type_entry
726            }
727
728            // If the type entry is a reference, then this definition is a
729            // simple alias to another type in this list of definitions
730            // (which may nor may not have already been converted). We
731            // simply create a newtype with that type ID.
732            TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
733                self,
734                type_name,
735                metadata,
736                type_id.clone(),
737                schema.clone(),
738            ),
739
740            TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
741
742            // For types that don't have names, this is effectively a type
743            // alias which we treat as a newtype.
744            _ => {
745                info!(
746                    "type alias {:?} {}\n{:?}",
747                    type_name,
748                    serde_json::to_string_pretty(&schema).unwrap(),
749                    metadata
750                );
751                let subtype_id = self.assign_type(type_entry);
752                TypeEntryNewtype::from_metadata(
753                    self,
754                    type_name,
755                    metadata,
756                    subtype_id,
757                    schema.clone(),
758                )
759            }
760        };
761        // TODO need a type alias?
762        if let Some(entry_name) = type_entry.name() {
763            self.name_to_id.insert(entry_name.clone(), type_id.clone());
764        }
765        self.id_to_entry.insert(type_id, type_entry);
766        Ok(())
767    }
768
769    /// Add a new type and return a type identifier that may be used in
770    /// function signatures or embedded within other types.
771    pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
772        self.add_type_with_name(schema, None)
773    }
774
775    /// Add a new type with a name hint and return a the components necessary
776    /// to use the type for various components of a function signature.
777    pub fn add_type_with_name(
778        &mut self,
779        schema: &Schema,
780        name_hint: Option<String>,
781    ) -> Result<TypeId> {
782        let base_id = self.next_id;
783
784        let name = match name_hint {
785            Some(s) => Name::Suggested(s),
786            None => Name::Unknown,
787        };
788        let (type_id, _) = self.id_for_schema(name, schema)?;
789
790        // Finalize all created types.
791        for index in base_id..self.next_id {
792            let type_id = TypeId(index);
793            let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
794            type_entry.finalize(self)?;
795            self.id_to_entry.insert(type_id, type_entry);
796        }
797
798        Ok(type_id)
799    }
800
801    /// Add all the types contained within a RootSchema including any
802    /// referenced types and the top-level type (if there is one and it has a
803    /// title).
804    pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
805        let RootSchema {
806            meta_schema: _,
807            schema,
808            definitions,
809        } = schema;
810
811        let mut defs = definitions
812            .into_iter()
813            .map(|(key, schema)| (RefKey::Def(key), schema))
814            .collect::<Vec<_>>();
815
816        // Does the root type have a name (otherwise... ignore it)
817        let root_type = schema
818            .metadata
819            .as_ref()
820            .and_then(|m| m.title.as_ref())
821            .is_some();
822
823        if root_type {
824            defs.push((RefKey::Root, schema.into()));
825        }
826
827        self.add_ref_types_impl(defs)?;
828
829        if root_type {
830            Ok(self.ref_to_id.get(&RefKey::Root).cloned())
831        } else {
832            Ok(None)
833        }
834    }
835
836    /// Get a type given its ID.
837    pub fn get_type(&self, type_id: &TypeId) -> Result<Type<'_>> {
838        let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
839        Ok(Type {
840            type_space: self,
841            type_entry,
842        })
843    }
844
845    /// Whether the generated code needs `chrono` crate.
846    pub fn uses_chrono(&self) -> bool {
847        self.uses_chrono
848    }
849
850    /// Whether the generated code needs [regress] crate.
851    pub fn uses_regress(&self) -> bool {
852        self.uses_regress
853    }
854
855    /// Whether the generated code needs [serde_json] crate.
856    pub fn uses_serde_json(&self) -> bool {
857        self.uses_serde_json
858    }
859
860    /// Whether the generated code needs `uuid` crate.
861    pub fn uses_uuid(&self) -> bool {
862        self.uses_uuid
863    }
864
865    /// Iterate over all types including those defined in this [TypeSpace] and
866    /// those referred to by those types.
867    pub fn iter_types(&self) -> impl Iterator<Item = Type<'_>> {
868        self.id_to_entry.values().map(move |type_entry| Type {
869            type_space: self,
870            type_entry,
871        })
872    }
873
874    /// All code for processed types.
875    pub fn to_stream(&self) -> TokenStream {
876        let mut output = OutputSpace::default();
877
878        // Add the error type we use for conversions; it's fine if this is
879        // unused.
880        output.add_item(
881            output::OutputSpaceMod::Error,
882            "",
883            quote! {
884                /// Error from a `TryFrom` or `FromStr` implementation.
885                pub struct ConversionError(::std::borrow::Cow<'static, str>);
886
887                impl ::std::error::Error for ConversionError {}
888                impl ::std::fmt::Display for ConversionError {
889                    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
890                        -> Result<(), ::std::fmt::Error>
891                    {
892                        ::std::fmt::Display::fmt(&self.0, f)
893                    }
894                }
895
896                impl ::std::fmt::Debug for ConversionError {
897                    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
898                        -> Result<(), ::std::fmt::Error>
899                    {
900                        ::std::fmt::Debug::fmt(&self.0, f)
901                    }
902                }
903                impl From<&'static str> for ConversionError {
904                    fn from(value: &'static str) -> Self {
905                        Self(value.into())
906                    }
907                }
908                impl From<String> for ConversionError {
909                    fn from(value: String) -> Self {
910                        Self(value.into())
911                    }
912                }
913            },
914        );
915
916        // Add all types.
917        self.id_to_entry
918            .values()
919            .for_each(|type_entry| type_entry.output(self, &mut output));
920
921        // Add all shared default functions.
922        self.defaults
923            .iter()
924            .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
925
926        output.into_stream()
927    }
928
929    /// Allocated the next TypeId.
930    fn assign(&mut self) -> TypeId {
931        let id = TypeId(self.next_id);
932        self.next_id += 1;
933        id
934    }
935
936    /// Assign a TypeId for a TypeEntry. This handles resolving references,
937    /// checking for duplicate type definitions (e.g. to make sure there aren't
938    /// two conflicting types of the same name), and deduplicates various
939    /// flavors of built-in types.
940    fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
941        if let TypeEntryDetails::Reference(type_id) = ty.details {
942            type_id
943        } else if let Some(name) = ty.name() {
944            // If there's already a type of this name, we make sure it's
945            // identical. Note that this covers all user-defined types.
946
947            // TODO there are many different choices we might make here
948            // that could differ depending on the texture of the schema.
949            // For example, a schema might use the string "Response" in a
950            // bunch of places and if that were the case we might expect
951            // them to be different and resolve that by renaming or scoping
952            // them in some way.
953            if let Some(type_id) = self.name_to_id.get(name) {
954                // TODO we'd like to verify that the type is structurally the
955                // same, but the types may not be functionally equal. This is a
956                // consequence of types being "finalized" after each type
957                // addition. This further emphasized the need for a more
958                // deliberate, multi-pass approach.
959                type_id.clone()
960            } else {
961                let type_id = self.assign();
962                self.name_to_id.insert(name.clone(), type_id.clone());
963                self.id_to_entry.insert(type_id.clone(), ty);
964                type_id
965            }
966        } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
967            type_id.clone()
968        } else {
969            let type_id = self.assign();
970            self.type_to_id.insert(ty.details.clone(), type_id.clone());
971            self.id_to_entry.insert(type_id.clone(), ty);
972            type_id
973        }
974    }
975
976    /// Convert a schema to a TypeEntry and assign it a TypeId.
977    ///
978    /// This is used for sub-types such as the type of an array or the types of
979    /// properties of a struct.
980    fn id_for_schema<'a>(
981        &mut self,
982        type_name: Name,
983        schema: &'a Schema,
984    ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
985        let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
986        if let Some(metadata) = metadata {
987            let default = metadata.default.clone().map(WrappedValue::new);
988            match &mut type_entry.details {
989                TypeEntryDetails::Enum(details) => {
990                    details.default = default;
991                }
992                TypeEntryDetails::Struct(details) => {
993                    details.default = default;
994                }
995                TypeEntryDetails::Newtype(details) => {
996                    details.default = default;
997                }
998                _ => (),
999            }
1000        }
1001        let type_id = self.assign_type(type_entry);
1002        Ok((type_id, metadata))
1003    }
1004
1005    /// Create an Option<T> from a pre-assigned TypeId and assign it an ID.
1006    fn id_to_option(&mut self, id: &TypeId) -> TypeId {
1007        self.assign_type(TypeEntryDetails::Option(id.clone()).into())
1008    }
1009
1010    // Create an Option<T> from a TypeEntry by assigning it type.
1011    fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
1012        TypeEntryDetails::Option(self.assign_type(ty)).into()
1013    }
1014
1015    /// Create a Box<T> from a pre-assigned TypeId and assign it an ID.
1016    fn id_to_box(&mut self, id: &TypeId) -> TypeId {
1017        self.assign_type(TypeEntryDetails::Box(id.clone()).into())
1018    }
1019}
1020
1021impl ToTokens for TypeSpace {
1022    fn to_tokens(&self, tokens: &mut TokenStream) {
1023        tokens.extend(self.to_stream())
1024    }
1025}
1026
1027impl Type<'_> {
1028    /// The name of the type as a String.
1029    pub fn name(&self) -> String {
1030        let Type {
1031            type_space,
1032            type_entry,
1033        } = self;
1034        type_entry.type_name(type_space)
1035    }
1036
1037    /// The identifier for the type as might be used for a function return or
1038    /// defining the type of a member of a struct..
1039    pub fn ident(&self) -> TokenStream {
1040        let Type {
1041            type_space,
1042            type_entry,
1043        } = self;
1044        type_entry.type_ident(type_space, &type_space.settings.type_mod)
1045    }
1046
1047    /// The identifier for the type as might be used for a parameter in a
1048    /// function signature. In general: simple types are the same as
1049    /// [Type::ident] and complex types prepend a `&`.
1050    pub fn parameter_ident(&self) -> TokenStream {
1051        let Type {
1052            type_space,
1053            type_entry,
1054        } = self;
1055        type_entry.type_parameter_ident(type_space, None)
1056    }
1057
1058    /// The identifier for the type as might be used for a parameter in a
1059    /// function signature along with a lifetime parameter. In general: simple
1060    /// types are the same as [Type::ident] and complex types prepend a
1061    /// `&'<lifetime>`.
1062    pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1063        let Type {
1064            type_space,
1065            type_entry,
1066        } = self;
1067        type_entry.type_parameter_ident(type_space, Some(lifetime))
1068    }
1069
1070    /// A textual description of the type appropriate for debug output.
1071    pub fn describe(&self) -> String {
1072        self.type_entry.describe()
1073    }
1074
1075    /// Get details about the type.
1076    pub fn details(&self) -> TypeDetails<'_> {
1077        match &self.type_entry.details {
1078            // Named user-defined types
1079            TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1080            TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1081            TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1082
1083            // Compound types
1084            TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1085            TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1086            TypeEntryDetails::Map(key_id, value_id) => {
1087                TypeDetails::Map(key_id.clone(), value_id.clone())
1088            }
1089            TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1090            TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1091            TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1092            TypeEntryDetails::Array(type_id, length) => {
1093                TypeDetails::Array(type_id.clone(), *length)
1094            }
1095
1096            // Builtin types
1097            TypeEntryDetails::Unit => TypeDetails::Unit,
1098            TypeEntryDetails::Native(TypeEntryNative {
1099                type_name: name, ..
1100            })
1101            | TypeEntryDetails::Integer(name)
1102            | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1103            TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1104            TypeEntryDetails::String => TypeDetails::String,
1105            TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1106
1107            // Only used during processing; shouldn't be visible at this point
1108            TypeEntryDetails::Reference(_) => unreachable!(),
1109        }
1110    }
1111
1112    /// Checks if the type has the associated impl.
1113    pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1114        let Type {
1115            type_space,
1116            type_entry,
1117        } = self;
1118        type_entry.has_impl(type_space, impl_name)
1119    }
1120
1121    /// Provides the the type identifier for the builder if one exists.
1122    pub fn builder(&self) -> Option<TokenStream> {
1123        let Type {
1124            type_space,
1125            type_entry,
1126        } = self;
1127
1128        if !type_space.settings.struct_builder {
1129            return None;
1130        }
1131
1132        match &type_entry.details {
1133            TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1134                match &type_space.settings.type_mod {
1135                    Some(type_mod) => {
1136                        let type_mod = format_ident!("{}", type_mod);
1137                        let type_name = format_ident!("{}", name);
1138                        Some(quote! { #type_mod :: builder :: #type_name })
1139                    }
1140                    None => {
1141                        let type_name = format_ident!("{}", name);
1142                        Some(quote! { builder :: #type_name })
1143                    }
1144                }
1145            }
1146            _ => None,
1147        }
1148    }
1149}
1150
1151impl<'a> TypeEnum<'a> {
1152    /// Get name and information of each enum variant.
1153    pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1154        self.variants_info().map(|info| (info.name, info.details))
1155    }
1156
1157    /// Get all information for each enum variant.
1158    pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1159        self.details.variants.iter().map(move |variant| {
1160            let details = match &variant.details {
1161                type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1162                // The distinction between a lone item variant and a tuple
1163                // variant with a single item is only relevant internally.
1164                type_entry::VariantDetails::Item(type_id) => {
1165                    TypeEnumVariant::Tuple(vec![type_id.clone()])
1166                }
1167                type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1168                type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1169                    properties
1170                        .iter()
1171                        .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1172                        .collect(),
1173                ),
1174            };
1175            TypeEnumVariantInfo {
1176                name: variant.ident_name.as_ref().unwrap(),
1177                description: variant.description.as_deref(),
1178                details,
1179            }
1180        })
1181    }
1182}
1183
1184impl<'a> TypeStruct<'a> {
1185    /// Get name and type of each property.
1186    pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1187        self.details
1188            .properties
1189            .iter()
1190            .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1191    }
1192
1193    /// Get all information about each struct property.
1194    pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo<'a>> {
1195        self.details
1196            .properties
1197            .iter()
1198            .map(move |prop| TypeStructPropInfo {
1199                name: prop.name.as_str(),
1200                description: prop.description.as_deref(),
1201                required: matches!(&prop.state, StructPropertyState::Required),
1202                type_id: prop.type_id.clone(),
1203            })
1204    }
1205}
1206
1207impl TypeNewtype<'_> {
1208    /// Get the inner type of the newtype struct.
1209    pub fn inner(&self) -> TypeId {
1210        self.details.type_id.clone()
1211    }
1212}
1213
1214#[cfg(test)]
1215mod tests {
1216    use schema::Schema;
1217    use schemars::{schema_for, JsonSchema};
1218    use serde::Serialize;
1219    use serde_json::json;
1220    use std::collections::HashSet;
1221
1222    use crate::{
1223        output::OutputSpace,
1224        test_util::validate_output,
1225        type_entry::{TypeEntryEnum, VariantDetails},
1226        Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1227    };
1228
1229    #[allow(dead_code)]
1230    #[derive(Serialize, JsonSchema)]
1231    struct Blah {
1232        blah: String,
1233    }
1234
1235    #[allow(dead_code)]
1236    #[derive(Serialize, JsonSchema)]
1237    #[serde(rename_all = "camelCase")]
1238    //#[serde(untagged)]
1239    //#[serde(tag = "type", content = "content")]
1240    enum E {
1241        /// aaa
1242        A,
1243        /// bee
1244        B,
1245        /// cee
1246        //C(Vec<String>),
1247        C(Blah),
1248        /// dee
1249        D {
1250            /// double D
1251            dd: String,
1252        },
1253        // /// eff
1254        // F(
1255        //     /// eff.0
1256        //     u32,
1257        //     /// eff.1
1258        //     u32,
1259        // ),
1260    }
1261
1262    #[allow(dead_code)]
1263    #[derive(JsonSchema)]
1264    #[serde(rename_all = "camelCase")]
1265    struct Foo {
1266        /// this is bar
1267        #[serde(default)]
1268        bar: Option<String>,
1269        baz_baz: i32,
1270        /// eeeeee!
1271        e: E,
1272    }
1273
1274    #[test]
1275    fn test_simple() {
1276        let schema = schema_for!(Foo);
1277        println!("{:#?}", schema);
1278        let mut type_space = TypeSpace::default();
1279        type_space.add_ref_types(schema.definitions).unwrap();
1280        let (ty, _) = type_space
1281            .convert_schema_object(
1282                Name::Unknown,
1283                &schemars::schema::Schema::Object(schema.schema.clone()),
1284                &schema.schema,
1285            )
1286            .unwrap();
1287
1288        println!("{:#?}", ty);
1289
1290        let mut output = OutputSpace::default();
1291        ty.output(&type_space, &mut output);
1292        println!("{}", output.into_stream());
1293
1294        for ty in type_space.id_to_entry.values() {
1295            println!("{:#?}", ty);
1296            let mut output = OutputSpace::default();
1297            ty.output(&type_space, &mut output);
1298            println!("{}", output.into_stream());
1299        }
1300    }
1301
1302    #[test]
1303    fn test_external_references() {
1304        let schema = json!({
1305            "$schema": "http://json-schema.org/draft-04/schema#",
1306            "definitions": {
1307                "somename": {
1308                    "$ref": "#/definitions/someothername",
1309                    "required": [ "someproperty" ]
1310                },
1311                "someothername": {
1312                    "type": "object",
1313                    "properties": {
1314                        "someproperty": {
1315                            "type": "string"
1316                        }
1317                    }
1318                }
1319            }
1320        });
1321        let schema = serde_json::from_value(schema).unwrap();
1322        println!("{:#?}", schema);
1323        let settings = TypeSpaceSettings::default();
1324        let mut type_space = TypeSpace::new(&settings);
1325        type_space.add_root_schema(schema).unwrap();
1326        let tokens = type_space.to_stream().to_string();
1327        println!("{}", tokens);
1328        assert!(tokens
1329            .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1330    }
1331
1332    #[test]
1333    fn test_convert_enum_string() {
1334        #[allow(dead_code)]
1335        #[derive(JsonSchema)]
1336        #[serde(rename_all = "camelCase")]
1337        enum SimpleEnum {
1338            DotCom,
1339            Grizz,
1340            Kenneth,
1341        }
1342
1343        let schema = schema_for!(SimpleEnum);
1344        println!("{:#?}", schema);
1345
1346        let mut type_space = TypeSpace::default();
1347        type_space.add_ref_types(schema.definitions).unwrap();
1348        let (ty, _) = type_space
1349            .convert_schema_object(
1350                Name::Unknown,
1351                &schemars::schema::Schema::Object(schema.schema.clone()),
1352                &schema.schema,
1353            )
1354            .unwrap();
1355
1356        match ty.details {
1357            TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1358                for variant in &variants {
1359                    assert_eq!(variant.details, VariantDetails::Simple);
1360                }
1361                let var_names = variants
1362                    .iter()
1363                    .map(|variant| variant.ident_name.as_ref().unwrap().clone())
1364                    .collect::<HashSet<_>>();
1365                assert_eq!(
1366                    var_names,
1367                    ["DotCom", "Grizz", "Kenneth",]
1368                        .iter()
1369                        .map(ToString::to_string)
1370                        .collect::<HashSet<_>>()
1371                );
1372            }
1373            _ => {
1374                let mut output = OutputSpace::default();
1375                ty.output(&type_space, &mut output);
1376                println!("{}", output.into_stream());
1377                panic!();
1378            }
1379        }
1380    }
1381
1382    #[test]
1383    fn test_string_enum_with_null() {
1384        let original_schema = json!({ "$ref": "xxx"});
1385        let enum_values = vec![
1386            json!("Shadrach"),
1387            json!("Meshach"),
1388            json!("Abednego"),
1389            json!(null),
1390        ];
1391
1392        let mut type_space = TypeSpace::default();
1393        let (te, _) = type_space
1394            .convert_enum_string(
1395                Name::Required("OnTheGo".to_string()),
1396                &serde_json::from_value(original_schema).unwrap(),
1397                &None,
1398                &enum_values,
1399                None,
1400            )
1401            .unwrap();
1402
1403        if let TypeEntryDetails::Option(id) = &te.details {
1404            let ote = type_space.id_to_entry.get(id).unwrap();
1405            if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1406                let variants = variants
1407                    .iter()
1408                    .map(|v| match v.details {
1409                        VariantDetails::Simple => v.ident_name.as_ref().unwrap().clone(),
1410                        _ => panic!("unexpected variant type"),
1411                    })
1412                    .collect::<HashSet<_>>();
1413
1414                assert_eq!(
1415                    variants,
1416                    enum_values
1417                        .iter()
1418                        .flat_map(|j| j.as_str().map(ToString::to_string))
1419                        .collect::<HashSet<_>>()
1420                );
1421            } else {
1422                panic!("not the sub-type we expected {:#?}", te)
1423            }
1424        } else {
1425            panic!("not the type we expected {:#?}", te)
1426        }
1427    }
1428
1429    #[test]
1430    fn test_alias() {
1431        #[allow(dead_code)]
1432        #[derive(JsonSchema, Schema)]
1433        struct Stuff(Vec<String>);
1434
1435        #[allow(dead_code)]
1436        #[derive(JsonSchema, Schema)]
1437        struct Things {
1438            a: String,
1439            b: Stuff,
1440        }
1441
1442        validate_output::<Things>();
1443    }
1444
1445    #[test]
1446    fn test_builder_name() {
1447        #[allow(dead_code)]
1448        #[derive(JsonSchema)]
1449        struct TestStruct {
1450            x: u32,
1451        }
1452
1453        let mut type_space = TypeSpace::default();
1454        let schema = schema_for!(TestStruct);
1455        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1456        let ty = type_space.get_type(&type_id).unwrap();
1457
1458        assert!(ty.builder().is_none());
1459
1460        let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1461        let schema = schema_for!(TestStruct);
1462        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1463        let ty = type_space.get_type(&type_id).unwrap();
1464
1465        assert_eq!(
1466            ty.builder().map(|ts| ts.to_string()),
1467            Some("builder :: TestStruct".to_string())
1468        );
1469
1470        let mut type_space = TypeSpace::new(
1471            TypeSpaceSettings::default()
1472                .with_type_mod("types")
1473                .with_struct_builder(true),
1474        );
1475        let schema = schema_for!(TestStruct);
1476        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1477        let ty = type_space.get_type(&type_id).unwrap();
1478
1479        assert_eq!(
1480            ty.builder().map(|ts| ts.to_string()),
1481            Some("types :: builder :: TestStruct".to_string())
1482        );
1483
1484        #[allow(dead_code)]
1485        #[derive(JsonSchema)]
1486        enum TestEnum {
1487            X,
1488            Y,
1489        }
1490        let mut type_space = TypeSpace::new(
1491            TypeSpaceSettings::default()
1492                .with_type_mod("types")
1493                .with_struct_builder(true),
1494        );
1495        let schema = schema_for!(TestEnum);
1496        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1497        let ty = type_space.get_type(&type_id).unwrap();
1498        assert!(ty.builder().is_none());
1499    }
1500}