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