typify_impl/
lib.rs

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