typify_impl/
lib.rs

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