typify_impl/
lib.rs

1// Copyright 2024 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    pub fn with_conversion<S: ToString, I: Iterator<Item = TypeSpaceImpl>>(
472        &mut self,
473        schema: schemars::schema::SchemaObject,
474        type_name: S,
475        impls: I,
476    ) -> &mut Self {
477        self.convert.push(TypeSpaceConversion {
478            schema,
479            type_name: type_name.to_string(),
480            impls: impls.collect(),
481        });
482        self
483    }
484
485    /// Type schemas may contain an extension (`x-rust-type`) that indicates
486    /// the corresponding Rust type within a particular crate. This function
487    /// changes the disposition regarding crates not otherwise specified via
488    /// [`Self::with_crate`]. The default value is `false`.
489    pub fn with_unknown_crates(&mut self, policy: UnknownPolicy) -> &mut Self {
490        self.unknown_crates = policy;
491        self
492    }
493
494    /// Type schemas may contain an extension (`x-rust-type`) that indicates
495    /// the corresponding Rust type within a particular crate. This extension
496    /// indicates the crate, version compatibility, type path, and type
497    /// parameters. This function modifies settings to use (rather than
498    /// generate) types from the given crate and version. The version should
499    /// precisely match the version of the crate that you expect as a
500    /// dependency.
501    pub fn with_crate<S1: ToString>(
502        &mut self,
503        crate_name: S1,
504        version: CrateVers,
505        rename: Option<&String>,
506    ) -> &mut Self {
507        self.crates.insert(
508            crate_name.to_string(),
509            CrateSpec {
510                version,
511                rename: rename.cloned(),
512            },
513        );
514        self
515    }
516
517    /// Specify the map-like type to be used in generated code.
518    ///
519    /// ## Requirements
520    ///
521    /// - An `is_empty` method that returns a boolean
522    /// - Two generic parameters, `K` and `V`
523    /// - [`Default`] + [`Clone`] + [`Debug`] +
524    ///   [`Serialize`][serde::Serialize] + [`Deserialize`][serde::Deserialize]
525    ///
526    /// ## Examples
527    ///
528    /// - [`::std::collections::HashMap`]
529    /// - [`::std::collections::BTreeMap`]
530    /// - [`::indexmap::IndexMap`](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html)
531    pub fn with_map_type<T: Into<MapType>>(&mut self, map_type: T) -> &mut Self {
532        self.map_type = map_type.into();
533        self
534    }
535}
536
537impl TypeSpacePatch {
538    /// Specify the new name for patched type.
539    pub fn with_rename<S: ToString>(&mut self, rename: S) -> &mut Self {
540        self.rename = Some(rename.to_string());
541        self
542    }
543
544    /// Specify an additional derive to apply to the patched type.
545    pub fn with_derive<S: ToString>(&mut self, derive: S) -> &mut Self {
546        self.derives.push(derive.to_string());
547        self
548    }
549}
550
551impl TypeSpace {
552    /// Create a new TypeSpace with custom settings
553    pub fn new(settings: &TypeSpaceSettings) -> Self {
554        let mut cache = SchemaCache::default();
555
556        settings.convert.iter().for_each(
557            |TypeSpaceConversion {
558                 schema,
559                 type_name,
560                 impls,
561             }| {
562                cache.insert(schema, type_name, impls);
563            },
564        );
565
566        Self {
567            settings: settings.clone(),
568            cache,
569            ..Default::default()
570        }
571    }
572
573    /// Add a collection of types that will be used as references. Regardless
574    /// of how these types are defined--*de novo* or built-in--each type will
575    /// appear in the final output as a struct, enum or newtype. This method
576    /// may be called multiple times, but collections of references must be
577    /// self-contained; in other words, a type in one invocation may not refer
578    /// to a type in another invocation.
579    // TODO on an error the TypeSpace is in a weird state; we, perhaps, create
580    // a child TypeSpace and then merge it in once all conversions hae
581    // succeeded.
582    pub fn add_ref_types<I, S>(&mut self, type_defs: I) -> Result<()>
583    where
584        I: IntoIterator<Item = (S, Schema)>,
585        S: AsRef<str>,
586    {
587        self.add_ref_types_impl(
588            type_defs
589                .into_iter()
590                .map(|(key, schema)| (RefKey::Def(key.as_ref().to_string()), schema)),
591        )
592    }
593
594    fn add_ref_types_impl<I>(&mut self, type_defs: I) -> Result<()>
595    where
596        I: IntoIterator<Item = (RefKey, Schema)>,
597    {
598        // Gather up all types to make things a little more convenient.
599        let definitions = type_defs.into_iter().collect::<Vec<_>>();
600
601        // Assign IDs to reference types before actually converting them. We'll
602        // need these in the case of forward (or circular) references.
603        let base_id = self.next_id;
604        let def_len = definitions.len() as u64;
605        self.next_id += def_len;
606
607        for (index, (ref_name, schema)) in definitions.iter().enumerate() {
608            self.ref_to_id
609                .insert(ref_name.clone(), TypeId(base_id + index as u64));
610            self.definitions.insert(ref_name.clone(), schema.clone());
611        }
612
613        // Convert all types; note that we use the type id assigned from the
614        // previous step because each type may create additional types. This
615        // effectively is doing the work of `add_type_with_name` but for a
616        // batch of types.
617        for (index, (ref_name, schema)) in definitions.into_iter().enumerate() {
618            info!(
619                "converting type: {:?} with schema {}",
620                ref_name,
621                serde_json::to_string(&schema).unwrap()
622            );
623
624            // Check for manually replaced types. Proceed with type conversion
625            // if there is none; use the specified type if there is.
626            let type_id = TypeId(base_id + index as u64);
627
628            let maybe_replace = match &ref_name {
629                RefKey::Root => None,
630                RefKey::Def(def_name) => {
631                    let check_name = sanitize(def_name, Case::Pascal);
632                    self.settings.replace.get(&check_name)
633                }
634            };
635
636            match maybe_replace {
637                None => {
638                    let type_name = if let RefKey::Def(name) = ref_name {
639                        Name::Required(name.clone())
640                    } else {
641                        Name::Unknown
642                    };
643                    self.convert_ref_type(type_name, schema, type_id)?
644                }
645
646                Some(replace_type) => {
647                    let type_entry = TypeEntry::new_native(
648                        replace_type.replace_type.clone(),
649                        &replace_type.impls.clone(),
650                    );
651                    self.id_to_entry.insert(type_id, type_entry);
652                }
653            }
654        }
655
656        // Eliminate cycles. It's sufficient to only start from referenced
657        // types as a reference is required to make a cycle.
658        self.break_cycles(base_id..base_id + def_len);
659
660        // Finalize all created types.
661        for index in base_id..self.next_id {
662            let type_id = TypeId(index);
663            let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
664            type_entry.finalize(self)?;
665            self.id_to_entry.insert(type_id, type_entry);
666        }
667
668        Ok(())
669    }
670
671    fn convert_ref_type(&mut self, type_name: Name, schema: Schema, type_id: TypeId) -> Result<()> {
672        let (mut type_entry, metadata) = self.convert_schema(type_name.clone(), &schema)?;
673        let default = metadata
674            .as_ref()
675            .and_then(|m| m.default.as_ref())
676            .cloned()
677            .map(WrappedValue::new);
678        let type_entry = match &mut type_entry.details {
679            // The types that are already named are good to go.
680            TypeEntryDetails::Enum(details) => {
681                details.default = default;
682                type_entry
683            }
684            TypeEntryDetails::Struct(details) => {
685                details.default = default;
686                type_entry
687            }
688            TypeEntryDetails::Newtype(details) => {
689                details.default = default;
690                type_entry
691            }
692
693            // If the type entry is a reference, then this definition is a
694            // simple alias to another type in this list of definitions
695            // (which may nor may not have already been converted). We
696            // simply create a newtype with that type ID.
697            TypeEntryDetails::Reference(type_id) => TypeEntryNewtype::from_metadata(
698                self,
699                type_name,
700                metadata,
701                type_id.clone(),
702                schema.clone(),
703            ),
704
705            TypeEntryDetails::Native(native) if native.name_match(&type_name) => type_entry,
706
707            // For types that don't have names, this is effectively a type
708            // alias which we treat as a newtype.
709            _ => {
710                info!(
711                    "type alias {:?} {}\n{:?}",
712                    type_name,
713                    serde_json::to_string_pretty(&schema).unwrap(),
714                    metadata
715                );
716                let subtype_id = self.assign_type(type_entry);
717                TypeEntryNewtype::from_metadata(
718                    self,
719                    type_name,
720                    metadata,
721                    subtype_id,
722                    schema.clone(),
723                )
724            }
725        };
726        // TODO need a type alias?
727        if let Some(entry_name) = type_entry.name() {
728            self.name_to_id.insert(entry_name.clone(), type_id.clone());
729        }
730        self.id_to_entry.insert(type_id, type_entry);
731        Ok(())
732    }
733
734    /// Add a new type and return a type identifier that may be used in
735    /// function signatures or embedded within other types.
736    pub fn add_type(&mut self, schema: &Schema) -> Result<TypeId> {
737        self.add_type_with_name(schema, None)
738    }
739
740    /// Add a new type with a name hint and return a the components necessary
741    /// to use the type for various components of a function signature.
742    pub fn add_type_with_name(
743        &mut self,
744        schema: &Schema,
745        name_hint: Option<String>,
746    ) -> Result<TypeId> {
747        let base_id = self.next_id;
748
749        let name = match name_hint {
750            Some(s) => Name::Suggested(s),
751            None => Name::Unknown,
752        };
753        let (type_id, _) = self.id_for_schema(name, schema)?;
754
755        // Finalize all created types.
756        for index in base_id..self.next_id {
757            let type_id = TypeId(index);
758            let mut type_entry = self.id_to_entry.get(&type_id).unwrap().clone();
759            type_entry.finalize(self)?;
760            self.id_to_entry.insert(type_id, type_entry);
761        }
762
763        Ok(type_id)
764    }
765
766    /// Add all the types contained within a RootSchema including any
767    /// referenced types and the top-level type (if there is one and it has a
768    /// title).
769    pub fn add_root_schema(&mut self, schema: RootSchema) -> Result<Option<TypeId>> {
770        let RootSchema {
771            meta_schema: _,
772            schema,
773            definitions,
774        } = schema;
775
776        let mut defs = definitions
777            .into_iter()
778            .map(|(key, schema)| (RefKey::Def(key), schema))
779            .collect::<Vec<_>>();
780
781        // Does the root type have a name (otherwise... ignore it)
782        let root_type = schema
783            .metadata
784            .as_ref()
785            .and_then(|m| m.title.as_ref())
786            .is_some();
787
788        if root_type {
789            defs.push((RefKey::Root, schema.into()));
790        }
791
792        self.add_ref_types_impl(defs)?;
793
794        if root_type {
795            Ok(self.ref_to_id.get(&RefKey::Root).cloned())
796        } else {
797            Ok(None)
798        }
799    }
800
801    /// Get a type given its ID.
802    pub fn get_type(&self, type_id: &TypeId) -> Result<Type> {
803        let type_entry = self.id_to_entry.get(type_id).ok_or(Error::InvalidTypeId)?;
804        Ok(Type {
805            type_space: self,
806            type_entry,
807        })
808    }
809
810    /// Whether the generated code needs `chrono` crate.
811    pub fn uses_chrono(&self) -> bool {
812        self.uses_chrono
813    }
814
815    /// Whether the generated code needs [regress] crate.
816    pub fn uses_regress(&self) -> bool {
817        self.uses_regress
818    }
819
820    /// Whether the generated code needs [serde_json] crate.
821    pub fn uses_serde_json(&self) -> bool {
822        self.uses_serde_json
823    }
824
825    /// Whether the generated code needs `uuid` crate.
826    pub fn uses_uuid(&self) -> bool {
827        self.uses_uuid
828    }
829
830    /// Iterate over all types including those defined in this [TypeSpace] and
831    /// those referred to by those types.
832    pub fn iter_types(&self) -> impl Iterator<Item = Type> {
833        self.id_to_entry.values().map(move |type_entry| Type {
834            type_space: self,
835            type_entry,
836        })
837    }
838
839    /// All code for processed types.
840    pub fn to_stream(&self) -> TokenStream {
841        let mut output = OutputSpace::default();
842
843        // Add the error type we use for conversions; it's fine if this is
844        // unused.
845        output.add_item(
846            output::OutputSpaceMod::Error,
847            "",
848            quote! {
849                /// Error from a TryFrom or FromStr implementation.
850                pub struct ConversionError(::std::borrow::Cow<'static, str>);
851
852                impl ::std::error::Error for ConversionError {}
853                impl ::std::fmt::Display for ConversionError {
854                    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
855                        -> Result<(), ::std::fmt::Error>
856                    {
857                        ::std::fmt::Display::fmt(&self.0, f)
858                    }
859                }
860
861                impl ::std::fmt::Debug for ConversionError {
862                    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>)
863                        -> Result<(), ::std::fmt::Error>
864                    {
865                        ::std::fmt::Debug::fmt(&self.0, f)
866                    }
867                }
868                impl From<&'static str> for ConversionError {
869                    fn from(value: &'static str) -> Self {
870                        Self(value.into())
871                    }
872                }
873                impl From<String> for ConversionError {
874                    fn from(value: String) -> Self {
875                        Self(value.into())
876                    }
877                }
878            },
879        );
880
881        // Add all types.
882        self.id_to_entry
883            .values()
884            .for_each(|type_entry| type_entry.output(self, &mut output));
885
886        // Add all shared default functions.
887        self.defaults
888            .iter()
889            .for_each(|x| output.add_item(output::OutputSpaceMod::Defaults, "", x.into()));
890
891        output.into_stream()
892    }
893
894    /// Allocated the next TypeId.
895    fn assign(&mut self) -> TypeId {
896        let id = TypeId(self.next_id);
897        self.next_id += 1;
898        id
899    }
900
901    /// Assign a TypeId for a TypeEntry. This handles resolving references,
902    /// checking for duplicate type definitions (e.g. to make sure there aren't
903    /// two conflicting types of the same name), and deduplicates various
904    /// flavors of built-in types.
905    fn assign_type(&mut self, ty: TypeEntry) -> TypeId {
906        if let TypeEntryDetails::Reference(type_id) = ty.details {
907            type_id
908        } else if let Some(name) = ty.name() {
909            // If there's already a type of this name, we make sure it's
910            // identical. Note that this covers all user-defined types.
911
912            // TODO there are many different choices we might make here
913            // that could differ depending on the texture of the schema.
914            // For example, a schema might use the string "Response" in a
915            // bunch of places and if that were the case we might expect
916            // them to be different and resolve that by renaming or scoping
917            // them in some way.
918            if let Some(type_id) = self.name_to_id.get(name) {
919                // TODO we'd like to verify that the type is structurally the
920                // same, but the types may not be functionally equal. This is a
921                // consequence of types being "finalized" after each type
922                // addition. This further emphasized the need for a more
923                // deliberate, multi-pass approach.
924                type_id.clone()
925            } else {
926                let type_id = self.assign();
927                self.name_to_id.insert(name.clone(), type_id.clone());
928                self.id_to_entry.insert(type_id.clone(), ty);
929                type_id
930            }
931        } else if let Some(type_id) = self.type_to_id.get(&ty.details) {
932            type_id.clone()
933        } else {
934            let type_id = self.assign();
935            self.type_to_id.insert(ty.details.clone(), type_id.clone());
936            self.id_to_entry.insert(type_id.clone(), ty);
937            type_id
938        }
939    }
940
941    /// Convert a schema to a TypeEntry and assign it a TypeId.
942    ///
943    /// This is used for sub-types such as the type of an array or the types of
944    /// properties of a struct.
945    fn id_for_schema<'a>(
946        &mut self,
947        type_name: Name,
948        schema: &'a Schema,
949    ) -> Result<(TypeId, &'a Option<Box<Metadata>>)> {
950        let (mut type_entry, metadata) = self.convert_schema(type_name, schema)?;
951        if let Some(metadata) = metadata {
952            let default = metadata.default.clone().map(WrappedValue::new);
953            match &mut type_entry.details {
954                TypeEntryDetails::Enum(details) => {
955                    details.default = default;
956                }
957                TypeEntryDetails::Struct(details) => {
958                    details.default = default;
959                }
960                TypeEntryDetails::Newtype(details) => {
961                    details.default = default;
962                }
963                _ => (),
964            }
965        }
966        let type_id = self.assign_type(type_entry);
967        Ok((type_id, metadata))
968    }
969
970    /// Create an Option<T> from a pre-assigned TypeId and assign it an ID.
971    fn id_to_option(&mut self, id: &TypeId) -> TypeId {
972        self.assign_type(TypeEntryDetails::Option(id.clone()).into())
973    }
974
975    // Create an Option<T> from a TypeEntry by assigning it type.
976    fn type_to_option(&mut self, ty: TypeEntry) -> TypeEntry {
977        TypeEntryDetails::Option(self.assign_type(ty)).into()
978    }
979
980    /// Create a Box<T> from a pre-assigned TypeId and assign it an ID.
981    fn id_to_box(&mut self, id: &TypeId) -> TypeId {
982        self.assign_type(TypeEntryDetails::Box(id.clone()).into())
983    }
984}
985
986impl ToTokens for TypeSpace {
987    fn to_tokens(&self, tokens: &mut TokenStream) {
988        tokens.extend(self.to_stream())
989    }
990}
991
992impl<'a> Type<'a> {
993    /// The name of the type as a String.
994    pub fn name(&self) -> String {
995        let Type {
996            type_space,
997            type_entry,
998        } = self;
999        type_entry.type_name(type_space)
1000    }
1001
1002    /// The identifier for the type as might be used for a function return or
1003    /// defining the type of a member of a struct..
1004    pub fn ident(&self) -> TokenStream {
1005        let Type {
1006            type_space,
1007            type_entry,
1008        } = self;
1009        type_entry.type_ident(type_space, &type_space.settings.type_mod)
1010    }
1011
1012    /// The identifier for the type as might be used for a parameter in a
1013    /// function signature. In general: simple types are the same as
1014    /// [Type::ident] and complex types prepend a `&`.
1015    pub fn parameter_ident(&self) -> TokenStream {
1016        let Type {
1017            type_space,
1018            type_entry,
1019        } = self;
1020        type_entry.type_parameter_ident(type_space, None)
1021    }
1022
1023    /// The identifier for the type as might be used for a parameter in a
1024    /// function signature along with a lifetime parameter. In general: simple
1025    /// types are the same as [Type::ident] and complex types prepend a
1026    /// `&'<lifetime>`.
1027    pub fn parameter_ident_with_lifetime(&self, lifetime: &str) -> TokenStream {
1028        let Type {
1029            type_space,
1030            type_entry,
1031        } = self;
1032        type_entry.type_parameter_ident(type_space, Some(lifetime))
1033    }
1034
1035    /// A textual description of the type appropriate for debug output.
1036    pub fn describe(&self) -> String {
1037        self.type_entry.describe()
1038    }
1039
1040    /// Get details about the type.
1041    pub fn details(&self) -> TypeDetails {
1042        match &self.type_entry.details {
1043            // Named user-defined types
1044            TypeEntryDetails::Enum(details) => TypeDetails::Enum(TypeEnum { details }),
1045            TypeEntryDetails::Struct(details) => TypeDetails::Struct(TypeStruct { details }),
1046            TypeEntryDetails::Newtype(details) => TypeDetails::Newtype(TypeNewtype { details }),
1047
1048            // Compound types
1049            TypeEntryDetails::Option(type_id) => TypeDetails::Option(type_id.clone()),
1050            TypeEntryDetails::Vec(type_id) => TypeDetails::Vec(type_id.clone()),
1051            TypeEntryDetails::Map(key_id, value_id) => {
1052                TypeDetails::Map(key_id.clone(), value_id.clone())
1053            }
1054            TypeEntryDetails::Set(type_id) => TypeDetails::Set(type_id.clone()),
1055            TypeEntryDetails::Box(type_id) => TypeDetails::Box(type_id.clone()),
1056            TypeEntryDetails::Tuple(types) => TypeDetails::Tuple(Box::new(types.iter().cloned())),
1057            TypeEntryDetails::Array(type_id, length) => {
1058                TypeDetails::Array(type_id.clone(), *length)
1059            }
1060
1061            // Builtin types
1062            TypeEntryDetails::Unit => TypeDetails::Unit,
1063            TypeEntryDetails::Native(TypeEntryNative {
1064                type_name: name, ..
1065            })
1066            | TypeEntryDetails::Integer(name)
1067            | TypeEntryDetails::Float(name) => TypeDetails::Builtin(name.as_str()),
1068            TypeEntryDetails::Boolean => TypeDetails::Builtin("bool"),
1069            TypeEntryDetails::String => TypeDetails::String,
1070            TypeEntryDetails::JsonValue => TypeDetails::Builtin("::serde_json::Value"),
1071
1072            // Only used during processing; shouldn't be visible at this point
1073            TypeEntryDetails::Reference(_) => unreachable!(),
1074        }
1075    }
1076
1077    /// Checks if the type has the associated impl.
1078    pub fn has_impl(&self, impl_name: TypeSpaceImpl) -> bool {
1079        let Type {
1080            type_space,
1081            type_entry,
1082        } = self;
1083        type_entry.has_impl(type_space, impl_name)
1084    }
1085
1086    /// Provides the the type identifier for the builder if one exists.
1087    pub fn builder(&self) -> Option<TokenStream> {
1088        let Type {
1089            type_space,
1090            type_entry,
1091        } = self;
1092
1093        if !type_space.settings.struct_builder {
1094            return None;
1095        }
1096
1097        match &type_entry.details {
1098            TypeEntryDetails::Struct(type_entry::TypeEntryStruct { name, .. }) => {
1099                match &type_space.settings.type_mod {
1100                    Some(type_mod) => {
1101                        let type_mod = format_ident!("{}", type_mod);
1102                        let type_name = format_ident!("{}", name);
1103                        Some(quote! { #type_mod :: builder :: #type_name })
1104                    }
1105                    None => {
1106                        let type_name = format_ident!("{}", name);
1107                        Some(quote! { builder :: #type_name })
1108                    }
1109                }
1110            }
1111            _ => None,
1112        }
1113    }
1114}
1115
1116impl<'a> TypeEnum<'a> {
1117    /// Get name and information of each enum variant.
1118    pub fn variants(&'a self) -> impl Iterator<Item = (&'a str, TypeEnumVariant<'a>)> {
1119        self.variants_info().map(|info| (info.name, info.details))
1120    }
1121
1122    /// Get all information for each enum variant.
1123    pub fn variants_info(&'a self) -> impl Iterator<Item = TypeEnumVariantInfo<'a>> {
1124        self.details.variants.iter().map(move |variant| {
1125            let details = match &variant.details {
1126                type_entry::VariantDetails::Simple => TypeEnumVariant::Simple,
1127                // The distinction between a lone item variant and a tuple
1128                // variant with a single item is only relevant internally.
1129                type_entry::VariantDetails::Item(type_id) => {
1130                    TypeEnumVariant::Tuple(vec![type_id.clone()])
1131                }
1132                type_entry::VariantDetails::Tuple(types) => TypeEnumVariant::Tuple(types.clone()),
1133                type_entry::VariantDetails::Struct(properties) => TypeEnumVariant::Struct(
1134                    properties
1135                        .iter()
1136                        .map(|prop| (prop.name.as_str(), prop.type_id.clone()))
1137                        .collect(),
1138                ),
1139            };
1140            TypeEnumVariantInfo {
1141                name: variant.name.as_str(),
1142                description: variant.description.as_deref(),
1143                details,
1144            }
1145        })
1146    }
1147}
1148
1149impl<'a> TypeStruct<'a> {
1150    /// Get name and type of each property.
1151    pub fn properties(&'a self) -> impl Iterator<Item = (&'a str, TypeId)> {
1152        self.details
1153            .properties
1154            .iter()
1155            .map(move |prop| (prop.name.as_str(), prop.type_id.clone()))
1156    }
1157
1158    /// Get all information about each struct property.
1159    pub fn properties_info(&'a self) -> impl Iterator<Item = TypeStructPropInfo> {
1160        self.details
1161            .properties
1162            .iter()
1163            .map(move |prop| TypeStructPropInfo {
1164                name: prop.name.as_str(),
1165                description: prop.description.as_deref(),
1166                required: matches!(&prop.state, StructPropertyState::Required),
1167                type_id: prop.type_id.clone(),
1168            })
1169    }
1170}
1171
1172impl<'a> TypeNewtype<'a> {
1173    /// Get the inner type of the newtype struct.
1174    pub fn inner(&self) -> TypeId {
1175        self.details.type_id.clone()
1176    }
1177}
1178
1179#[cfg(test)]
1180mod tests {
1181    use schema::Schema;
1182    use schemars::{schema_for, JsonSchema};
1183    use serde::Serialize;
1184    use serde_json::json;
1185    use std::collections::HashSet;
1186
1187    use crate::{
1188        output::OutputSpace,
1189        test_util::validate_output,
1190        type_entry::{TypeEntryEnum, VariantDetails},
1191        Name, TypeEntryDetails, TypeSpace, TypeSpaceSettings,
1192    };
1193
1194    #[allow(dead_code)]
1195    #[derive(Serialize, JsonSchema)]
1196    struct Blah {
1197        blah: String,
1198    }
1199
1200    #[allow(dead_code)]
1201    #[derive(Serialize, JsonSchema)]
1202    #[serde(rename_all = "camelCase")]
1203    //#[serde(untagged)]
1204    //#[serde(tag = "type", content = "content")]
1205    enum E {
1206        /// aaa
1207        A,
1208        /// bee
1209        B,
1210        /// cee
1211        //C(Vec<String>),
1212        C(Blah),
1213        /// dee
1214        D {
1215            /// double D
1216            dd: String,
1217        },
1218        // /// eff
1219        // F(
1220        //     /// eff.0
1221        //     u32,
1222        //     /// eff.1
1223        //     u32,
1224        // ),
1225    }
1226
1227    #[allow(dead_code)]
1228    #[derive(JsonSchema)]
1229    #[serde(rename_all = "camelCase")]
1230    struct Foo {
1231        /// this is bar
1232        #[serde(default)]
1233        bar: Option<String>,
1234        baz_baz: i32,
1235        /// eeeeee!
1236        e: E,
1237    }
1238
1239    #[test]
1240    fn test_simple() {
1241        let schema = schema_for!(Foo);
1242        println!("{:#?}", schema);
1243        let mut type_space = TypeSpace::default();
1244        type_space.add_ref_types(schema.definitions).unwrap();
1245        let (ty, _) = type_space
1246            .convert_schema_object(
1247                Name::Unknown,
1248                &schemars::schema::Schema::Object(schema.schema.clone()),
1249                &schema.schema,
1250            )
1251            .unwrap();
1252
1253        println!("{:#?}", ty);
1254
1255        let mut output = OutputSpace::default();
1256        ty.output(&type_space, &mut output);
1257        println!("{}", output.into_stream());
1258
1259        for ty in type_space.id_to_entry.values() {
1260            println!("{:#?}", ty);
1261            let mut output = OutputSpace::default();
1262            ty.output(&type_space, &mut output);
1263            println!("{}", output.into_stream());
1264        }
1265    }
1266
1267    #[test]
1268    fn test_external_references() {
1269        let schema = json!({
1270            "$schema": "http://json-schema.org/draft-04/schema#",
1271            "definitions": {
1272                "somename": {
1273                    "$ref": "#/definitions/someothername",
1274                    "required": [ "someproperty" ]
1275                },
1276                "someothername": {
1277                    "type": "object",
1278                    "properties": {
1279                        "someproperty": {
1280                            "type": "string"
1281                        }
1282                    }
1283                }
1284            }
1285        });
1286        let schema = serde_json::from_value(schema).unwrap();
1287        println!("{:#?}", schema);
1288        let settings = TypeSpaceSettings::default();
1289        let mut type_space = TypeSpace::new(&settings);
1290        type_space.add_root_schema(schema).unwrap();
1291        let tokens = type_space.to_stream().to_string();
1292        println!("{}", tokens);
1293        assert!(tokens
1294            .contains(" pub struct Somename { pub someproperty : :: std :: string :: String , }"))
1295    }
1296
1297    #[test]
1298    fn test_convert_enum_string() {
1299        #[allow(dead_code)]
1300        #[derive(JsonSchema)]
1301        #[serde(rename_all = "camelCase")]
1302        enum SimpleEnum {
1303            DotCom,
1304            Grizz,
1305            Kenneth,
1306        }
1307
1308        let schema = schema_for!(SimpleEnum);
1309        println!("{:#?}", schema);
1310
1311        let mut type_space = TypeSpace::default();
1312        type_space.add_ref_types(schema.definitions).unwrap();
1313        let (ty, _) = type_space
1314            .convert_schema_object(
1315                Name::Unknown,
1316                &schemars::schema::Schema::Object(schema.schema.clone()),
1317                &schema.schema,
1318            )
1319            .unwrap();
1320
1321        match ty.details {
1322            TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) => {
1323                for variant in &variants {
1324                    assert_eq!(variant.details, VariantDetails::Simple);
1325                }
1326                let var_names = variants
1327                    .iter()
1328                    .map(|variant| variant.name.clone())
1329                    .collect::<HashSet<_>>();
1330                assert_eq!(
1331                    var_names,
1332                    ["DotCom", "Grizz", "Kenneth",]
1333                        .iter()
1334                        .map(ToString::to_string)
1335                        .collect::<HashSet<_>>()
1336                );
1337            }
1338            _ => {
1339                let mut output = OutputSpace::default();
1340                ty.output(&type_space, &mut output);
1341                println!("{}", output.into_stream());
1342                panic!();
1343            }
1344        }
1345    }
1346
1347    #[test]
1348    fn test_string_enum_with_null() {
1349        let original_schema = json!({ "$ref": "xxx"});
1350        let enum_values = vec![
1351            json!("Shadrach"),
1352            json!("Meshach"),
1353            json!("Abednego"),
1354            json!(null),
1355        ];
1356
1357        let mut type_space = TypeSpace::default();
1358        let (te, _) = type_space
1359            .convert_enum_string(
1360                Name::Required("OnTheGo".to_string()),
1361                &serde_json::from_value(original_schema).unwrap(),
1362                &None,
1363                &enum_values,
1364                None,
1365            )
1366            .unwrap();
1367
1368        if let TypeEntryDetails::Option(id) = &te.details {
1369            let ote = type_space.id_to_entry.get(id).unwrap();
1370            if let TypeEntryDetails::Enum(TypeEntryEnum { variants, .. }) = &ote.details {
1371                let variants = variants
1372                    .iter()
1373                    .map(|v| match v.details {
1374                        VariantDetails::Simple => v.name.clone(),
1375                        _ => panic!("unexpected variant type"),
1376                    })
1377                    .collect::<HashSet<_>>();
1378
1379                assert_eq!(
1380                    variants,
1381                    enum_values
1382                        .iter()
1383                        .flat_map(|j| j.as_str().map(ToString::to_string))
1384                        .collect::<HashSet<_>>()
1385                );
1386            } else {
1387                panic!("not the sub-type we expected {:#?}", te)
1388            }
1389        } else {
1390            panic!("not the type we expected {:#?}", te)
1391        }
1392    }
1393
1394    #[test]
1395    fn test_alias() {
1396        #[allow(dead_code)]
1397        #[derive(JsonSchema, Schema)]
1398        struct Stuff(Vec<String>);
1399
1400        #[allow(dead_code)]
1401        #[derive(JsonSchema, Schema)]
1402        struct Things {
1403            a: String,
1404            b: Stuff,
1405        }
1406
1407        validate_output::<Things>();
1408    }
1409
1410    #[test]
1411    fn test_builder_name() {
1412        #[allow(dead_code)]
1413        #[derive(JsonSchema)]
1414        struct TestStruct {
1415            x: u32,
1416        }
1417
1418        let mut type_space = TypeSpace::default();
1419        let schema = schema_for!(TestStruct);
1420        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1421        let ty = type_space.get_type(&type_id).unwrap();
1422
1423        assert!(ty.builder().is_none());
1424
1425        let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
1426        let schema = schema_for!(TestStruct);
1427        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1428        let ty = type_space.get_type(&type_id).unwrap();
1429
1430        assert_eq!(
1431            ty.builder().map(|ts| ts.to_string()),
1432            Some("builder :: TestStruct".to_string())
1433        );
1434
1435        let mut type_space = TypeSpace::new(
1436            TypeSpaceSettings::default()
1437                .with_type_mod("types")
1438                .with_struct_builder(true),
1439        );
1440        let schema = schema_for!(TestStruct);
1441        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1442        let ty = type_space.get_type(&type_id).unwrap();
1443
1444        assert_eq!(
1445            ty.builder().map(|ts| ts.to_string()),
1446            Some("types :: builder :: TestStruct".to_string())
1447        );
1448
1449        #[allow(dead_code)]
1450        #[derive(JsonSchema)]
1451        enum TestEnum {
1452            X,
1453            Y,
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!(TestEnum);
1461        let type_id = type_space.add_root_schema(schema).unwrap().unwrap();
1462        let ty = type_space.get_type(&type_id).unwrap();
1463        assert!(ty.builder().is_none());
1464    }
1465}