diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 8734ba24..b4abd22f 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -5,9 +5,8 @@ edition = "2021" publish = false [dependencies] -specta = { path = "../specta", features = ["derive"] } +specta = { path = "../specta", features = ["derive", "export"] } specta-typescript = { path = "../specta-typescript" } -specta-util = { path = "../specta-util", features = ["export"] } [lints] workspace = true diff --git a/examples/examples/export.rs b/examples/examples/export.rs index 27186c92..cc1087f8 100644 --- a/examples/examples/export.rs +++ b/examples/examples/export.rs @@ -14,16 +14,16 @@ pub struct TypeTwo { fn main() { // Export as string - let string = specta_util::export().export(Typescript::default()).unwrap(); + let string = specta::export().export(Typescript::default()).unwrap(); println!("{string}"); // Export to file - specta_util::export() + specta::export() .export_to(Typescript::default(), "./bindings.ts") .unwrap(); // Override the export configuration. - specta_util::export() + specta::export() .export_to( Typescript::default() // Be aware this won't be typesafe unless your using a ser/deserializer that converts BigInt types to a number. diff --git a/specta-go/src/lib.rs b/specta-go/src/lib.rs index 7af00801..703b1f62 100644 --- a/specta-go/src/lib.rs +++ b/specta-go/src/lib.rs @@ -5,11 +5,14 @@ html_favicon_url = "https://github.com/oscartbeaumont/specta/raw/main/.github/logo-128.png" )] -use specta::{datatype::DataType, Generics, Type, TypeMap}; +use specta::{datatype::DataType, Generics, Type, TypeCollection}; /// TODO pub fn export() -> Result { - datatype(&T::inline(&mut TypeMap::default(), Generics::Definition)) + datatype(&T::inline( + &mut TypeCollection::default(), + Generics::Definition, + )) } fn datatype(t: &DataType) -> Result { diff --git a/specta-jsdoc/src/lib.rs b/specta-jsdoc/src/lib.rs index 3f900488..4133a656 100644 --- a/specta-jsdoc/src/lib.rs +++ b/specta-jsdoc/src/lib.rs @@ -7,7 +7,7 @@ use std::{borrow::Cow, path::Path}; -use specta::{Language, TypeMap}; +use specta::{Language, TypeCollection}; use specta_typescript::{BigIntExportBehavior, CommentFormatterFn, FormatterFn}; // TODO: Ensure this is up to our `Typescript` exporters standards. @@ -79,7 +79,7 @@ impl Language for JSDoc { type Error = specta_typescript::ExportError; // TODO: Custom error type // TODO: Make this properly export JSDoc - fn export(&self, type_map: TypeMap) -> Result { + fn export(&self, _type_map: &TypeCollection) -> Result { todo!("Coming soon..."); // let mut out = self.0.header.to_string(); // if !self.0.remove_default_header { diff --git a/specta-kotlin/src/lib.rs b/specta-kotlin/src/lib.rs index d7c27eec..b748e7a8 100644 --- a/specta-kotlin/src/lib.rs +++ b/specta-kotlin/src/lib.rs @@ -7,12 +7,15 @@ use specta::{ datatype::{DataType, PrimitiveType}, - Generics, Type, TypeMap, + Generics, Type, TypeCollection, }; /// TODO pub fn export() -> Result { - datatype(&T::inline(&mut TypeMap::default(), Generics::Definition)) + datatype(&T::inline( + &mut TypeCollection::default(), + Generics::Definition, + )) } fn datatype(t: &DataType) -> Result { diff --git a/specta-macros/src/specta.rs b/specta-macros/src/specta.rs index 42668463..f9b707eb 100644 --- a/specta-macros/src/specta.rs +++ b/specta-macros/src/specta.rs @@ -106,7 +106,7 @@ pub fn attribute(item: proc_macro::TokenStream) -> syn::Result` (@export_fn; $function:path) => {{ - fn export(type_map: &mut #crate_ref::TypeMap) -> #crate_ref::datatype::Function { + fn export(type_map: &mut #crate_ref::TypeCollection) -> #crate_ref::datatype::Function { #crate_ref::internal::get_fn_datatype( $function as fn(#(#arg_signatures),*) -> _, #function_asyncness, diff --git a/specta-macros/src/type/mod.rs b/specta-macros/src/type/mod.rs index ac0b5888..2b900b34 100644 --- a/specta-macros/src/type/mod.rs +++ b/specta-macros/src/type/mod.rs @@ -39,7 +39,6 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result syn::Result>(); + #crate_ref::export::internal::register::<#ident<#(#generic_params),*>>(); } } }); @@ -138,7 +137,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result #crate_ref::datatype::DataType { + fn inline(type_map: &mut #crate_ref::TypeCollection, generics: #crate_ref::Generics) -> #crate_ref::datatype::DataType { let generics = match generics { #crate_ref::Generics::Definition => DEFINITION_GENERICS, #crate_ref::Generics::Provided(generics) => generics, @@ -147,7 +146,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result #crate_ref::datatype::reference::Reference { + fn reference(type_map: &mut #crate_ref::TypeCollection, generics: &[#crate_ref::datatype::DataType]) -> #crate_ref::datatype::reference::Reference { #reference } } @@ -158,7 +157,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result #crate_ref::datatype::NamedDataType { + fn named_data_type(type_map: &mut #crate_ref::TypeCollection, generics: &[#crate_ref::datatype::DataType]) -> #crate_ref::datatype::NamedDataType { #crate_ref::internal::construct::named_data_type( #name.into(), #comments.into(), @@ -169,7 +168,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result #crate_ref::datatype::NamedDataType { + fn definition_named_data_type(type_map: &mut #crate_ref::TypeCollection) -> #crate_ref::datatype::NamedDataType { #crate_ref::internal::construct::named_data_type( #name.into(), #comments.into(), diff --git a/specta-rust/src/lib.rs b/specta-rust/src/lib.rs index 35c7595b..194ea1a2 100644 --- a/specta-rust/src/lib.rs +++ b/specta-rust/src/lib.rs @@ -5,11 +5,14 @@ html_favicon_url = "https://github.com/oscartbeaumont/specta/raw/main/.github/logo-128.png" )] -use specta::{datatype::DataType, Generics, Type, TypeMap}; +use specta::{datatype::DataType, Generics, Type, TypeCollection}; /// TODO pub fn export() -> Result { - datatype(&T::inline(&mut TypeMap::default(), Generics::Definition)) + datatype(&T::inline( + &mut TypeCollection::default(), + Generics::Definition, + )) } fn datatype(t: &DataType) -> Result { diff --git a/specta-serde/src/lib.rs b/specta-serde/src/lib.rs index 0fc17837..2392c598 100644 --- a/specta-serde/src/lib.rs +++ b/specta-serde/src/lib.rs @@ -14,7 +14,7 @@ use specta::{ DataType, EnumRepr, EnumType, EnumVariants, LiteralType, PrimitiveType, StructFields, }, internal::{resolve_generics, skip_fields, skip_fields_named}, - SpectaID, TypeMap, + SpectaID, TypeCollection, }; // TODO: The error should show a path to the type causing the issue like the BigInt error reporting. @@ -32,13 +32,13 @@ pub enum SerdeError { /// Check that a [DataType] is a valid for Serde. /// /// This can be used by exporters which wanna do export-time checks that all types are compatible with Serde formats. -pub fn is_valid_ty(dt: &DataType, type_map: &TypeMap) -> Result<(), SerdeError> { +pub fn is_valid_ty(dt: &DataType, type_map: &TypeCollection) -> Result<(), SerdeError> { is_valid_ty_internal(dt, type_map, &mut Default::default()) } fn is_valid_ty_internal( dt: &DataType, - type_map: &TypeMap, + type_map: &TypeCollection, checked_references: &mut HashSet, ) -> Result<(), SerdeError> { match dt { @@ -106,7 +106,7 @@ fn is_valid_ty_internal( } // Typescript: Must be assignable to `string | number | symbol` says Typescript. -fn is_valid_map_key(key_ty: &DataType, type_map: &TypeMap) -> Result<(), SerdeError> { +fn is_valid_map_key(key_ty: &DataType, type_map: &TypeCollection) -> Result<(), SerdeError> { match key_ty { DataType::Any => Ok(()), DataType::Primitive(ty) => match ty { @@ -171,7 +171,7 @@ fn is_valid_map_key(key_ty: &DataType, type_map: &TypeMap) -> Result<(), SerdeEr } // Serde does not allow serializing a variant of certain types of enum's. -fn validate_enum(e: &EnumType, type_map: &TypeMap) -> Result<(), SerdeError> { +fn validate_enum(e: &EnumType, type_map: &TypeCollection) -> Result<(), SerdeError> { // You can't `#[serde(skip)]` your way to an empty enum. let valid_variants = e.variants().iter().filter(|(_, v)| !v.skip()).count(); if valid_variants == 0 && !e.variants().is_empty() { @@ -187,7 +187,7 @@ fn validate_enum(e: &EnumType, type_map: &TypeMap) -> Result<(), SerdeError> { } // Checks for specially internally tagged enums. -fn validate_internally_tag_enum(e: &EnumType, type_map: &TypeMap) -> Result<(), SerdeError> { +fn validate_internally_tag_enum(e: &EnumType, type_map: &TypeCollection) -> Result<(), SerdeError> { for (_variant_name, variant) in e.variants() { match &variant.inner() { EnumVariants::Unit => {} @@ -215,7 +215,7 @@ fn validate_internally_tag_enum(e: &EnumType, type_map: &TypeMap) -> Result<(), // Which makes sense when you can't represent `{ "type": "A" } & string` in a single JSON value. fn validate_internally_tag_enum_datatype( ty: &DataType, - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Result<(), SerdeError> { match ty { // `serde_json::Any` can be *technically* be either valid or invalid based on the actual data but we are being strict and reject it. diff --git a/specta-swift/src/lib.rs b/specta-swift/src/lib.rs index 57152f52..ad5ee086 100644 --- a/specta-swift/src/lib.rs +++ b/specta-swift/src/lib.rs @@ -7,12 +7,15 @@ use specta::{ datatype::{DataType, PrimitiveType}, - Generics, Type, TypeMap, + Generics, Type, TypeCollection, }; /// TODO pub fn export() -> Result { - datatype(&T::inline(&mut TypeMap::default(), Generics::Definition)) + datatype(&T::inline( + &mut TypeCollection::default(), + Generics::Definition, + )) } fn datatype(t: &DataType) -> Result { diff --git a/specta-typescript/src/js_doc.rs b/specta-typescript/src/js_doc.rs index 00d3a52f..ea7a50d3 100644 --- a/specta-typescript/src/js_doc.rs +++ b/specta-typescript/src/js_doc.rs @@ -2,13 +2,17 @@ use std::borrow::Borrow; use specta::{ datatype::{DeprecatedType, GenericType}, - TypeMap, + TypeCollection, }; use typescript::CommentFormatterArgs; use super::*; -pub fn typedef_named_datatype(cfg: &Typescript, typ: &NamedDataType, type_map: &TypeMap) -> Output { +pub fn typedef_named_datatype( + cfg: &Typescript, + typ: &NamedDataType, + type_map: &TypeCollection, +) -> Output { typedef_named_datatype_inner( &ExportContext { cfg, @@ -24,7 +28,7 @@ pub fn typedef_named_datatype(cfg: &Typescript, typ: &NamedDataType, type_map: & fn typedef_named_datatype_inner( ctx: &ExportContext, typ: &NamedDataType, - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Output { let name = typ.name(); let docs = typ.docs(); diff --git a/specta-typescript/src/lib.rs b/specta-typescript/src/lib.rs index 5ba898ca..f9bb9476 100644 --- a/specta-typescript/src/lib.rs +++ b/specta-typescript/src/lib.rs @@ -27,7 +27,7 @@ use specta::datatype::{ }; use specta::{ internal::{detect_duplicate_type_names, skip_fields, skip_fields_named, NonSkipField}, - Generics, NamedType, Type, TypeMap, + Generics, NamedType, Type, TypeCollection, }; use specta_serde::is_valid_ty; @@ -47,7 +47,7 @@ pub fn export_ref(_: &T, conf: &Typescript) -> Output { /// /// Eg. `export type Foo = { demo: string; };` pub fn export(conf: &Typescript) -> Output { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); let named_data_type = T::definition_named_data_type(&mut type_map); is_valid_ty(&named_data_type.inner, &type_map)?; let result = export_named_datatype(conf, &named_data_type, &type_map); @@ -70,7 +70,7 @@ pub fn inline_ref(_: &T, conf: &Typescript) -> Output { /// /// Eg. `{ demo: string; };` pub fn inline(conf: &Typescript) -> Output { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); let ty = T::inline(&mut type_map, Generics::NONE); is_valid_ty(&ty, &type_map)?; let result = datatype(conf, &FunctionResultVariant::Value(ty.clone()), &type_map); @@ -85,7 +85,11 @@ pub fn inline(conf: &Typescript) -> Output { /// Convert a DataType to a TypeScript string /// /// Eg. `export Name = { demo: string; }` -pub fn export_named_datatype(conf: &Typescript, typ: &NamedDataType, type_map: &TypeMap) -> Output { +pub fn export_named_datatype( + conf: &Typescript, + typ: &NamedDataType, + type_map: &TypeCollection, +) -> Output { // TODO: Duplicate type name detection? is_valid_ty(&typ.inner, type_map)?; @@ -126,7 +130,11 @@ fn inner_comments( format!("{prefix}{comments}{other}") } -fn export_datatype_inner(ctx: ExportContext, typ: &NamedDataType, type_map: &TypeMap) -> Output { +fn export_datatype_inner( + ctx: ExportContext, + typ: &NamedDataType, + type_map: &TypeCollection, +) -> Output { let name = typ.name(); let docs = typ.docs(); let ext = typ.ext(); @@ -166,7 +174,11 @@ fn export_datatype_inner(ctx: ExportContext, typ: &NamedDataType, type_map: &Typ /// Convert a DataType to a TypeScript string /// /// Eg. `{ demo: string; }` -pub fn datatype(conf: &Typescript, typ: &FunctionResultVariant, type_map: &TypeMap) -> Output { +pub fn datatype( + conf: &Typescript, + typ: &FunctionResultVariant, + type_map: &TypeCollection, +) -> Output { // TODO: Duplicate type name detection? let mut s = String::new(); @@ -192,7 +204,7 @@ macro_rules! primitive_def { pub(crate) fn datatype_inner( ctx: ExportContext, typ: &FunctionResultVariant, - type_map: &TypeMap, + type_map: &TypeCollection, s: &mut String, ) -> Result<()> { let typ = match typ { @@ -390,7 +402,7 @@ pub(crate) fn datatype_inner( fn unnamed_fields_datatype( ctx: ExportContext, fields: &[NonSkipField], - type_map: &TypeMap, + type_map: &TypeCollection, s: &mut String, ) -> Result<()> { Ok(match fields { @@ -439,7 +451,7 @@ fn unnamed_fields_datatype( }) } -fn tuple_datatype(ctx: ExportContext, tuple: &TupleType, type_map: &TypeMap) -> Output { +fn tuple_datatype(ctx: ExportContext, tuple: &TupleType, type_map: &TypeCollection) -> Output { match &tuple.elements()[..] { [] => Ok(NULL.to_string()), tys => Ok(format!( @@ -465,7 +477,7 @@ fn struct_datatype( ctx: ExportContext, key: &str, strct: &StructType, - type_map: &TypeMap, + type_map: &TypeCollection, s: &mut String, ) -> Result<()> { Ok(match &strct.fields() { @@ -550,7 +562,7 @@ fn struct_datatype( fn enum_variant_datatype( ctx: ExportContext, - type_map: &TypeMap, + type_map: &TypeCollection, name: Cow<'static, str>, variant: &EnumVariant, ) -> Result> { @@ -630,7 +642,7 @@ fn enum_variant_datatype( fn enum_datatype( ctx: ExportContext, e: &EnumType, - type_map: &TypeMap, + type_map: &TypeCollection, s: &mut String, ) -> Result<()> { if e.variants().is_empty() { @@ -804,7 +816,7 @@ fn object_field_to_ts( ctx: ExportContext, key: Cow<'static, str>, (field, ty): NonSkipField, - type_map: &TypeMap, + type_map: &TypeCollection, s: &mut String, ) -> Result<()> { let field_name_safe = sanitise_key(key, false); @@ -876,7 +888,7 @@ pub(crate) fn sanitise_type_name(ctx: ExportContext, loc: NamedLocation, ident: fn validate_type_for_tagged_intersection( ctx: ExportContext, ty: DataType, - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Result { match ty { DataType::Any @@ -934,7 +946,7 @@ fn validate_type_for_tagged_intersection( ctx, type_map .get(r.sid()) - .expect("TypeMap should have been populated by now") + .expect("TypeCollection should have been populated by now") .inner .clone(), type_map, diff --git a/specta-typescript/src/typescript.rs b/specta-typescript/src/typescript.rs index 1cb1839f..66bc52fb 100644 --- a/specta-typescript/src/typescript.rs +++ b/specta-typescript/src/typescript.rs @@ -4,7 +4,7 @@ use std::{ path::{Path, PathBuf}, }; -use specta::{datatype::DeprecatedType, Language, TypeMap}; +use specta::{datatype::DeprecatedType, Language, TypeCollection}; use specta_serde::is_valid_ty; use crate::{comments, detect_duplicate_type_names, export_named_datatype, ExportError}; @@ -54,8 +54,6 @@ pub enum BigIntExportBehavior { pub struct Typescript { /// The file's header pub header: Cow<'static, str>, - /// Should we remove the default header? - pub remove_default_header: bool, /// How BigInts should be exported. pub bigint: BigIntExportBehavior, /// How comments should be rendered. @@ -68,7 +66,6 @@ impl Default for Typescript { fn default() -> Self { Self { header: Cow::Borrowed(""), - remove_default_header: false, bigint: Default::default(), comment_exporter: Some(comments::js_doc), formatter: None, @@ -90,13 +87,6 @@ impl Typescript { self } - // TODO: Only keep this is TS stays responsible for exporting which it probs won't. - /// Removes the default Specta header from the output. - pub fn remove_default_header(mut self) -> Self { - self.remove_default_header = true; - self - } - /// Configure the BigInt handling behaviour pub fn bigint(mut self, bigint: BigIntExportBehavior) -> Self { self.bigint = bigint; @@ -132,17 +122,18 @@ impl Typescript { impl Language for Typescript { type Error = ExportError; - fn export(&self, type_map: TypeMap) -> Result { + fn export(&self, type_map: &TypeCollection) -> Result { let mut out = self.header.to_string(); - if !self.remove_default_header { - out += "// This file has been generated by Specta. DO NOT EDIT.\n\n"; + if !self.header.is_empty() { + out.push('\n'); } + out.push('\n'); if let Some((ty_name, l0, l1)) = detect_duplicate_type_names(&type_map).into_iter().next() { return Err(ExportError::DuplicateTypeName(ty_name, l0, l1)); } - for (_, ty) in type_map.iter() { + for (_, ty) in type_map.into_iter() { is_valid_ty(&ty.inner, &type_map)?; out += &export_named_datatype(self, ty, &type_map)?; diff --git a/specta-util/Cargo.toml b/specta-util/Cargo.toml index 8fbcd01c..ebea9efb 100644 --- a/specta-util/Cargo.toml +++ b/specta-util/Cargo.toml @@ -17,8 +17,6 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = [] -## Support for collecting up a global type map -export = ["dep:ctor", "specta-macros/DO_NOT_USE_export"] # TODO: Can we remove this? serde = [] @@ -29,5 +27,4 @@ workspace = true [dependencies] specta = { version = "=2.0.0-rc.20", path = "../specta" } specta-macros = { version = "=2.0.0-rc.17", path = "../specta-macros", default-features = false, optional = true } -ctor = { version = "0.2.8", default-features = false, optional = true } serde = "1.0.204" # TODO: Can we remove this or at least make it optional behind the `serde` flag diff --git a/specta-util/src/lib.rs b/specta-util/src/lib.rs index 02a23fb3..d1039c58 100644 --- a/specta-util/src/lib.rs +++ b/specta-util/src/lib.rs @@ -5,24 +5,12 @@ html_favicon_url = "https://github.com/oscartbeaumont/specta/raw/main/.github/logo-128.png" )] -/// Provides the global type store and a method to export them to other languages. -#[cfg(feature = "export")] -#[cfg_attr(docsrs, doc(cfg(feature = "export")))] -#[doc(hidden)] -pub mod export; - -#[cfg(feature = "export")] -#[cfg_attr(docsrs, doc(cfg(feature = "export")))] -pub use export::export; - #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] mod selection; mod static_types; -mod type_collection; #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] // pub use selection::selection; pub use static_types::{Any, Unknown}; -pub use type_collection::TypeCollection; diff --git a/specta-util/src/static_types.rs b/specta-util/src/static_types.rs index 9c12c299..6a88d5f8 100644 --- a/specta-util/src/static_types.rs +++ b/specta-util/src/static_types.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use specta::{datatype::DataType, Generics, Type, TypeMap}; +use specta::{datatype::DataType, Generics, Type, TypeCollection}; /// Easily convert a non-Specta type into a Specta compatible type. /// This will be typed as `any` in Typescript. @@ -34,7 +34,7 @@ use specta::{datatype::DataType, Generics, Type, TypeMap}; pub struct Any(T); impl Type for Any { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Any } } @@ -97,7 +97,7 @@ impl serde::Serialize for Any { pub struct Unknown(T); impl Type for Unknown { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Unknown } } diff --git a/specta-util/src/type_collection.rs b/specta-util/src/type_collection.rs deleted file mode 100644 index c9e81b9f..00000000 --- a/specta-util/src/type_collection.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::{borrow::Borrow, collections::HashMap, path::Path}; - -use specta::{datatype::NamedDataType, Language, NamedType, SpectaID, TypeMap}; - -/// Define a set of types which can be exported together -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TypeCollection { - types: HashMap NamedDataType>, -} - -impl Default for TypeCollection { - fn default() -> Self { - Self { - types: HashMap::new(), - } - } -} - -impl TypeCollection { - #[allow(unused)] - pub(crate) fn from_raw(types: HashMap NamedDataType>) -> Self { - Self { types } - } - - /// Join another type collection into this one. - pub fn extend(&mut self, collection: impl Borrow) -> &mut Self { - self.types.extend(collection.borrow().types.iter()); - self - } - - /// Register a type with the collection. - pub fn register(&mut self) -> &mut Self { - self.types - .insert(T::sid(), |type_map| T::definition_named_data_type(type_map)); - self - } - - /// Export all the types in the collection into the given type map. - pub fn collect(&self, mut type_map: &mut TypeMap) { - for (sid, export) in self.types.iter() { - let dt = export(&mut type_map); - type_map.insert(*sid, dt); - } - } - - /// TODO - pub fn export(&self, language: L) -> Result { - let mut type_map = TypeMap::default(); - self.collect(&mut type_map); - language.export(type_map) - } - - /// TODO - pub fn export_to( - &self, - language: L, - path: impl AsRef, - ) -> Result<(), L::Error> { - std::fs::write(path, self.export(language)?).map_err(Into::into) - } -} diff --git a/specta-zod/src/lib.rs b/specta-zod/src/lib.rs index 61bf2d22..60cd6543 100644 --- a/specta-zod/src/lib.rs +++ b/specta-zod/src/lib.rs @@ -42,7 +42,7 @@ pub fn export_ref(_: &T, conf: &ExportConfig) -> Output { /// /// Eg. `export const Foo = z.object({ demo: string; });` pub fn export(conf: &ExportConfig) -> Output { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); let named_data_type = T::definition_named_data_type(&mut type_map); // is_valid_ty(&named_data_type.inner, &type_map)?; let result = export_named_datatype(conf, &named_data_type, &type_map); @@ -65,7 +65,7 @@ pub fn inline_ref(_: &T, conf: &ExportConfig) -> Output { /// /// Eg. `z.object({ demo: z.string() });` pub fn inline(conf: &ExportConfig) -> Output { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); let ty = T::inline(&mut type_map, specta::Generics::Definition); // is_valid_ty(&ty, &type_map)?; let result = datatype(conf, &ty, &type_map); @@ -83,7 +83,7 @@ pub fn inline(conf: &ExportConfig) -> Output { pub fn export_named_datatype( conf: &ExportConfig, typ: &NamedDataType, - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Output { // TODO: Duplicate type name detection? @@ -126,7 +126,11 @@ fn inner_comments( format!("{prefix}{comments}{other}") } -fn export_datatype_inner(ctx: ExportContext, typ: &NamedDataType, type_map: &TypeMap) -> Output { +fn export_datatype_inner( + ctx: ExportContext, + typ: &NamedDataType, + type_map: &TypeCollection, +) -> Output { let ctx = ctx.with( typ.ext() .map(|v| PathItem::TypeExtended(typ.name().clone(), *v.impl_location())) @@ -156,7 +160,7 @@ fn export_datatype_inner(ctx: ExportContext, typ: &NamedDataType, type_map: &Typ /// Convert a DataType to a Zod validator /// /// Eg. `z.object({ demo: z.string(); })` -pub fn datatype(conf: &ExportConfig, typ: &DataType, type_map: &TypeMap) -> Output { +pub fn datatype(conf: &ExportConfig, typ: &DataType, type_map: &TypeCollection) -> Output { // TODO: Duplicate type name detection? datatype_inner( @@ -170,7 +174,11 @@ pub fn datatype(conf: &ExportConfig, typ: &DataType, type_map: &TypeMap) -> Outp ) } -pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &TypeMap) -> Output { +pub(crate) fn datatype_inner( + ctx: ExportContext, + typ: &DataType, + type_map: &TypeCollection, +) -> Output { Ok(match &typ { DataType::Any => ANY.into(), DataType::Unknown => UNKNOWN.into(), @@ -278,7 +286,7 @@ pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &Type fn unnamed_fields_datatype( ctx: ExportContext, fields: &[NonSkipField], - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Output { match fields { [(field, ty)] => Ok(inner_comments( @@ -305,7 +313,7 @@ fn unnamed_fields_datatype( } } -fn tuple_datatype(ctx: ExportContext, tuple: &TupleType, type_map: &TypeMap) -> Output { +fn tuple_datatype(ctx: ExportContext, tuple: &TupleType, type_map: &TypeCollection) -> Output { match &tuple.elements()[..] { [] => Ok(NULL.into()), tys => Ok(format!( @@ -318,7 +326,12 @@ fn tuple_datatype(ctx: ExportContext, tuple: &TupleType, type_map: &TypeMap) -> } } -fn struct_datatype(ctx: ExportContext, key: &str, s: &StructType, type_map: &TypeMap) -> Output { +fn struct_datatype( + ctx: ExportContext, + key: &str, + s: &StructType, + type_map: &TypeCollection, +) -> Output { match &s.fields() { StructFields::Unit => Ok(NULL.into()), StructFields::Unnamed(s) => { @@ -406,7 +419,7 @@ fn struct_datatype(ctx: ExportContext, key: &str, s: &StructType, type_map: &Typ fn enum_variant_datatype( ctx: ExportContext, - type_map: &TypeMap, + type_map: &TypeCollection, name: Cow<'static, str>, variant: &EnumVariant, ) -> Result> { @@ -471,7 +484,7 @@ fn enum_variant_datatype( } } -fn enum_datatype(ctx: ExportContext, e: &EnumType, type_map: &TypeMap) -> Output { +fn enum_datatype(ctx: ExportContext, e: &EnumType, type_map: &TypeCollection) -> Output { if e.variants().is_empty() { return Ok(NEVER.to_string()); } @@ -651,7 +664,7 @@ fn object_field_to_ts( ctx: ExportContext, key: Cow<'static, str>, (field, ty): NonSkipField, - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Output { let field_name_safe = sanitise_key(key, false); @@ -714,7 +727,7 @@ pub(crate) fn sanitise_type_name(ctx: ExportContext, loc: NamedLocation, ident: fn validate_type_for_tagged_intersection( ctx: ExportContext, ty: DataType, - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Result { match ty { DataType::Any @@ -772,7 +785,7 @@ fn validate_type_for_tagged_intersection( ctx, type_map .get(r.sid()) - .expect("TypeMap should have been populated by now") + .expect("TypeCollection should have been populated by now") .inner .clone(), type_map, diff --git a/specta/Cargo.toml b/specta/Cargo.toml index c1cc2015..e225226e 100644 --- a/specta/Cargo.toml +++ b/specta/Cargo.toml @@ -23,6 +23,9 @@ default = [] ## Derive macros derive = ["dep:specta-macros"] +## Support for collecting up a global type map +export = ["dep:ctor", "specta-macros/DO_NOT_USE_export"] + #! Features ## Support for exporting the types of Rust functions. function = ["specta-macros?/DO_NOT_USE_function", "dep:paste"] @@ -88,6 +91,8 @@ specta-macros = { version = "=2.0.0-rc.17", optional = true, path = "../specta-m paste = { version = "1", optional = true } thiserror = "1.0.61" +ctor = { version = "0.2.8", default-features = false, optional = true } + # TODO: To be deprecated specta1 = { package = "specta", version = "1", optional = true, default-features = false } serde = { version = "1.0.204", optional = true, default-features = false, features = ["derive"] } diff --git a/specta/src/datatype/named.rs b/specta/src/datatype/named.rs index 4dc7295b..0c2947f7 100644 --- a/specta/src/datatype/named.rs +++ b/specta/src/datatype/named.rs @@ -37,6 +37,7 @@ pub struct NamedDataType { pub(crate) ext: Option, /// the actual type definition. // This field is public because we match on it in flattening code. // TODO: Review if this can be made private when reviewing the flattening logic/error handling + // TODO: This field is also used by rspc to construct the `Procedures` type. pub inner: DataType, } diff --git a/specta/src/datatype/reference.rs b/specta/src/datatype/reference.rs index 23336ea6..5846fb9b 100644 --- a/specta/src/datatype/reference.rs +++ b/specta/src/datatype/reference.rs @@ -1,6 +1,6 @@ //! Helpers for generating [Type::reference] implementations. -use crate::{Generics, NamedType, Type, TypeMap}; +use crate::{Generics, NamedType, Type, TypeCollection}; use super::{DataType, DataTypeReference}; @@ -12,13 +12,16 @@ pub struct Reference { pub inner: DataType, } -pub fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { +pub fn inline(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: T::inline(type_map, Generics::Provided(generics)), } } -pub fn reference(type_map: &mut TypeMap, reference: DataTypeReference) -> Reference { +pub fn reference( + type_map: &mut TypeCollection, + reference: DataTypeReference, +) -> Reference { let sid = T::sid(); if type_map.map.get(&sid).is_none() { diff --git a/specta-util/src/export.rs b/specta/src/export.rs similarity index 67% rename from specta-util/src/export.rs rename to specta/src/export.rs index 18b1052a..a5adc024 100644 --- a/specta-util/src/export.rs +++ b/specta/src/export.rs @@ -3,22 +3,26 @@ use std::{ sync::{Mutex, OnceLock, PoisonError}, }; -use specta::{datatype::NamedDataType, NamedType, SpectaID, TypeMap}; - -use crate::TypeCollection; +use crate::{datatype::NamedDataType, NamedType, SpectaID, TypeCollection}; // Global type store for collecting custom types to export. -static TYPES: OnceLock NamedDataType>>> = +static TYPES: OnceLock NamedDataType>>> = OnceLock::new(); /// Get the global type store containing all registered types. pub fn export() -> TypeCollection { - let type_map = TYPES + // TODO: Make `TYPES` should just hold a `TypeCollection` directly??? + let types = TYPES .get_or_init(Default::default) .lock() .unwrap_or_else(PoisonError::into_inner); - TypeCollection::from_raw(type_map.clone()) + let mut map = TypeCollection::default(); + for (id, export) in types.iter() { + let dt = export(&mut map); + map.insert(*id, dt); + } + map } #[doc(hidden)] diff --git a/specta/src/function/arg.rs b/specta/src/function/arg.rs index b68e1639..1ca58f24 100644 --- a/specta/src/function/arg.rs +++ b/specta/src/function/arg.rs @@ -1,4 +1,4 @@ -use crate::{datatype::DataType, Type, TypeMap}; +use crate::{datatype::DataType, Type, TypeCollection}; /// Implemented by types that can be used as an argument in a function annotated with /// [`specta`](crate::specta). @@ -7,11 +7,11 @@ pub trait FunctionArg { /// /// Some argument types should be ignored (eg. when doing dependency injection), /// so the value is optional. - fn to_datatype(type_map: &mut TypeMap) -> Option; + fn to_datatype(type_map: &mut TypeCollection) -> Option; } impl FunctionArg for T { - fn to_datatype(type_map: &mut TypeMap) -> Option { + fn to_datatype(type_map: &mut TypeCollection) -> Option { Some(T::reference(type_map, &[]).inner) } } diff --git a/specta/src/function/mod.rs b/specta/src/function/mod.rs index 40bdd3e3..1b90b8e8 100644 --- a/specta/src/function/mod.rs +++ b/specta/src/function/mod.rs @@ -26,7 +26,7 @@ pub(crate) use specta_fn::SpectaFn; /// } /// /// fn main() { -/// let typ = fn_datatype!(some_function)(&mut TypeMap::default()); +/// let typ = fn_datatype!(some_function)(&mut TypeCollection::default()); /// /// assert_eq!(typ.name(), "some_function"); /// assert_eq!(typ.args().len(), 2); @@ -72,9 +72,9 @@ macro_rules! _fn_datatype_internal { } /// Collects function types into a [`Vec`], -/// and all downstream types into a [`TypeMap`](crate::TypeMap) instance. +/// and all downstream types into a [`TypeCollection`](crate::TypeCollection) instance. /// -/// Specifying a `type_map` argument allows a custom [`TypeMap`] to be used. +/// Specifying a `type_map` argument allows a custom [`TypeCollection`] to be used. /// /// # Examples /// @@ -87,21 +87,21 @@ macro_rules! _fn_datatype_internal { /// } /// /// fn main() { -/// let functions = function::collect_functions![some_function](&mut TypeMap::default()); +/// let functions = function::collect_functions![some_function](&mut TypeCollection::default()); /// } /// ```` #[doc(hidden)] #[macro_export] macro_rules! _collect_functions { ($(,)?) => {{ - fn export(_: &mut $crate::TypeMap) -> Vec<$crate::datatype::Function> { + fn export(_: &mut $crate::TypeCollection) -> Vec<$crate::datatype::Function> { vec![] } export }}; ($($b:tt $(:: $($p:ident)? $(<$($g:path),*>)? )* ),* $(,)?) => {{ - fn export(type_map: &mut $crate::TypeMap) -> Vec<$crate::datatype::Function> { + fn export(type_map: &mut $crate::TypeCollection) -> Vec<$crate::datatype::Function> { vec![ $($crate::function::fn_datatype!($b $($(::$p)? $(::<$($g),*>)? )* )(type_map)),* ] diff --git a/specta/src/function/result.rs b/specta/src/function/result.rs index f6fdf4c8..4ca19458 100644 --- a/specta/src/function/result.rs +++ b/specta/src/function/result.rs @@ -1,18 +1,18 @@ use std::future::Future; -use crate::{datatype::FunctionResultVariant, Type, TypeMap}; +use crate::{datatype::FunctionResultVariant, Type, TypeCollection}; /// Implemented by types that can be returned from a function annotated with /// [`specta`](crate::specta). pub trait FunctionResult { /// Gets the type of the result as a [`DataType`]. - fn to_datatype(type_map: &mut TypeMap) -> FunctionResultVariant; + fn to_datatype(type_map: &mut TypeCollection) -> FunctionResultVariant; } #[doc(hidden)] pub enum FunctionValueMarker {} impl FunctionResult for T { - fn to_datatype(type_map: &mut TypeMap) -> FunctionResultVariant { + fn to_datatype(type_map: &mut TypeCollection) -> FunctionResultVariant { FunctionResultVariant::Value(T::reference(type_map, &[]).inner) } } @@ -20,7 +20,7 @@ impl FunctionResult for T { #[doc(hidden)] pub enum FunctionResultMarker {} impl FunctionResult for Result { - fn to_datatype(type_map: &mut TypeMap) -> FunctionResultVariant { + fn to_datatype(type_map: &mut TypeCollection) -> FunctionResultVariant { FunctionResultVariant::Result( T::reference(type_map, &[]).inner, E::reference(type_map, &[]).inner, @@ -35,7 +35,7 @@ where F: Future, F::Output: Type, { - fn to_datatype(type_map: &mut TypeMap) -> FunctionResultVariant { + fn to_datatype(type_map: &mut TypeCollection) -> FunctionResultVariant { FunctionResultVariant::Value(F::Output::reference(type_map, &[]).inner) } } @@ -48,7 +48,7 @@ where T: Type, E: Type, { - fn to_datatype(type_map: &mut TypeMap) -> FunctionResultVariant { + fn to_datatype(type_map: &mut TypeCollection) -> FunctionResultVariant { FunctionResultVariant::Result( T::reference(type_map, &[]).inner, E::reference(type_map, &[]).inner, diff --git a/specta/src/function/specta_fn.rs b/specta/src/function/specta_fn.rs index fdd0f7da..f8f6a792 100644 --- a/specta/src/function/specta_fn.rs +++ b/specta/src/function/specta_fn.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::{datatype::DeprecatedType, datatype::Function, TypeMap}; +use crate::{datatype::DeprecatedType, datatype::Function, TypeCollection}; use super::{FunctionArg, FunctionResult}; @@ -12,7 +12,7 @@ pub trait SpectaFn { fn to_datatype( asyncness: bool, name: Cow<'static, str>, - type_map: &mut TypeMap, + type_map: &mut TypeCollection, fields: &[Cow<'static, str>], docs: Cow<'static, str>, deprecated: Option, @@ -26,7 +26,7 @@ impl> SpectaFn, - type_map: &mut TypeMap, + type_map: &mut TypeCollection, _fields: &[Cow<'static, str>], docs: Cow<'static, str>, deprecated: Option, @@ -54,7 +54,7 @@ macro_rules! impl_typed_command { fn to_datatype( asyncness: bool, name: Cow<'static, str>, - type_map: &mut TypeMap, + type_map: &mut TypeCollection, fields: &[Cow<'static, str>], docs: Cow<'static, str>, deprecated: Option, diff --git a/specta/src/internal.rs b/specta/src/internal.rs index 8fd824e5..09ea3cec 100644 --- a/specta/src/internal.rs +++ b/specta/src/internal.rs @@ -14,7 +14,7 @@ pub use paste::paste; use crate::{ datatype::{DataType, EnumVariants, Field, GenericType, List, Map, StructFields}, - Generics, ImplLocation, SpectaID, Type, TypeMap, + Generics, ImplLocation, SpectaID, Type, TypeCollection, }; /// Functions used to construct `crate::datatype` types (they have private fields so can't be constructed directly). @@ -205,7 +205,11 @@ pub fn skip_fields_named<'a>( } #[track_caller] -pub fn flatten(sid: SpectaID, type_map: &mut TypeMap, generics: &[DataType]) -> DataType { +pub fn flatten( + sid: SpectaID, + type_map: &mut TypeCollection, + generics: &[DataType], +) -> DataType { type_map.flatten_stack.push(sid); #[allow(clippy::panic)] @@ -233,7 +237,7 @@ mod functions { _: T, asyncness: bool, name: Cow<'static, str>, - type_map: &mut TypeMap, + type_map: &mut TypeCollection, fields: &[Cow<'static, str>], docs: Cow<'static, str>, deprecated: Option, @@ -329,12 +333,12 @@ pub fn resolve_generics(mut dt: DataType, generics: &Vec<(GenericType, DataType) // TODO: This should go /// post process the type map to detect duplicate type names pub fn detect_duplicate_type_names( - type_map: &TypeMap, + type_map: &TypeCollection, ) -> Vec<(Cow<'static, str>, ImplLocation, ImplLocation)> { let mut errors = Vec::new(); - let mut map = HashMap::with_capacity(type_map.len()); - for (sid, dt) in type_map.iter() { + let mut map = HashMap::with_capacity(type_map.into_iter().len()); + for (sid, dt) in type_map.into_iter() { if let Some(ext) = &dt.ext { if let Some((existing_sid, existing_impl_location)) = map.insert(dt.name.clone(), (sid, ext.impl_location)) diff --git a/specta/src/internal/interop.rs b/specta/src/internal/interop.rs index 48f94b1f..0c0affcc 100644 --- a/specta/src/internal/interop.rs +++ b/specta/src/internal/interop.rs @@ -6,7 +6,7 @@ use specta1::NamedDataType; use crate::{ datatype::{DataType, DeprecatedType, LiteralType, PrimitiveType}, - TypeMap, + TypeCollection, }; /// Allow for conversion between Specta v2 and v1 data types. @@ -369,8 +369,8 @@ pub fn specta_v2_to_v1(datatype: DataType) -> specta1::DataType { } } -pub fn specta_v2_type_map_to_v1_type_defs(defs: TypeMap, type_map: &mut specta1::TypeDefs) { - for (sid, dt) in defs.iter() { +pub fn specta_v2_type_map_to_v1_type_defs(defs: TypeCollection, type_map: &mut specta1::TypeDefs) { + for (sid, dt) in defs.into_iter() { let dtv1 = specta_v2_to_v1(dt.inner.clone()); let sid = specta1::r#type::internal_sid_hash("specta1", "", sid.type_name); type_map.insert( diff --git a/specta/src/language.rs b/specta/src/language.rs index a00a5426..09fd2ab6 100644 --- a/specta/src/language.rs +++ b/specta/src/language.rs @@ -1,6 +1,6 @@ use std::path::Path; -use crate::TypeMap; +use crate::TypeCollection; /// TODO /// @@ -11,7 +11,7 @@ pub trait Language { type Error: std::error::Error + From; /// TODO - fn export(&self, type_map: TypeMap) -> Result; + fn export(&self, type_map: &TypeCollection) -> Result; /// TODO // TODO: Not sure I love this here but it's for Tauri Specta. @@ -22,7 +22,7 @@ pub trait Language { impl Language for &T { type Error = T::Error; - fn export(&self, type_map: TypeMap) -> Result { + fn export(&self, type_map: &TypeCollection) -> Result { (*self).export(type_map) } diff --git a/specta/src/lib.rs b/specta/src/lib.rs index 96842b4d..13ca9dd3 100644 --- a/specta/src/lib.rs +++ b/specta/src/lib.rs @@ -6,6 +6,10 @@ )] pub mod datatype; +#[cfg(feature = "export")] +#[cfg_attr(docsrs, doc(cfg(feature = "export")))] +#[doc(hidden)] +pub mod export; #[cfg(feature = "function")] #[cfg_attr(docsrs, doc(cfg(feature = "function")))] pub mod function; @@ -14,16 +18,21 @@ pub mod internal; mod language; mod specta_id; mod r#type; -mod type_map; +mod type_collection; // TODO: Can we just move the trait here or `#[doc(inline)]` pub use r#type::{Flatten, Generics, NamedType, Type}; // #[doc(inline)] pub use specta_id::{ImplLocation, SpectaID}; -pub use type_map::TypeMap; +pub use type_collection::TypeCollection; pub use language::Language; +#[doc(inline)] +#[cfg(feature = "export")] +#[cfg_attr(docsrs, doc(cfg(feature = "export")))] +pub use export::export; + #[doc(inline)] #[cfg(feature = "derive")] #[cfg_attr(docsrs, doc(cfg(feature = "derive")))] diff --git a/specta/src/type.rs b/specta/src/type.rs index 28b879f2..fa233fbf 100644 --- a/specta/src/type.rs +++ b/specta/src/type.rs @@ -3,7 +3,7 @@ use crate::{ reference::{self, Reference}, DataType, NamedDataType, }, - SpectaID, TypeMap, + SpectaID, TypeCollection, }; mod impls; @@ -45,13 +45,13 @@ pub trait Type { /// Returns the definition of a type using the provided generics. /// /// This should be only implemented via the [`Type`](derive@crate::Type) macro. - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType; + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType; /// Generates a datatype corresponding to a reference to this type, /// as determined by its category. Getting a reference to a type implies that /// it should belong in the type map (since it has to be referenced from somewhere), /// so the output of [`definition`](crate::Type::definition) will be put into the type map. - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { reference::inline::(type_map, generics) } } @@ -64,11 +64,11 @@ pub trait NamedType: Type { // TODO: Should take `Generics` instead of `&[DataType]` but I plan to remove this trait so not fixing it for now. /// this is equivalent to [Type::inline] but returns a [NamedDataType] instead. - fn named_data_type(type_map: &mut TypeMap, generics: &[DataType]) -> NamedDataType; + fn named_data_type(type_map: &mut TypeCollection, generics: &[DataType]) -> NamedDataType; // TODO: Just remove this method given we removed `Type::definition` /// this is equivalent to [Type::definition] but returns a [NamedDataType] instead. - fn definition_named_data_type(type_map: &mut TypeMap) -> NamedDataType; + fn definition_named_data_type(type_map: &mut TypeCollection) -> NamedDataType; } /// A marker trait for compile-time validation of which types can be flattened. diff --git a/specta/src/type/impls.rs b/specta/src/type/impls.rs index 76ae24c2..3e46175b 100644 --- a/specta/src/type/impls.rs +++ b/specta/src/type/impls.rs @@ -115,7 +115,7 @@ impl<'a, T: Type> Type for &'a [T] { } impl Type for [T; N] { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { DataType::List(List { ty: Box::new( // TODO: This is cursed. Fix it properly!!! @@ -129,7 +129,7 @@ impl Type for [T; N] { }) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: DataType::List(List { ty: Box::new( @@ -147,7 +147,7 @@ impl Type for [T; N] { } impl Type for Option { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { let mut ty = None; if let Generics::Provided(generics) = &generics { ty = generics.get(0).cloned() @@ -159,7 +159,7 @@ impl Type for Option { })) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: DataType::Nullable(Box::new( generics @@ -172,7 +172,7 @@ impl Type for Option { } impl Type for std::marker::PhantomData { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Literal(LiteralType::None) } } @@ -183,7 +183,7 @@ const _: () = { internal::construct::impl_location("specta/src/type/impls.rs:234:10"); impl Type for std::convert::Infallible { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Enum(internal::construct::r#enum( "Infallible".into(), internal::construct::sid("Infallible", "::todo:4:10"), @@ -193,7 +193,7 @@ const _: () = { vec![], )) } - fn reference(type_map: &mut TypeMap, _: &[DataType]) -> reference::Reference { + fn reference(type_map: &mut TypeCollection, _: &[DataType]) -> reference::Reference { let generics = vec![]; reference::reference::( type_map, @@ -211,7 +211,7 @@ const _: () = { internal::construct::sid("Infallible", "::todo:234:10") } - fn named_data_type(type_map: &mut TypeMap, generics: &[DataType]) -> NamedDataType { + fn named_data_type(type_map: &mut TypeCollection, generics: &[DataType]) -> NamedDataType { internal::construct::named_data_type( "Infallible".into(), "".into(), @@ -221,7 +221,7 @@ const _: () = { ::inline(type_map, Generics::Provided(generics)), ) } - fn definition_named_data_type(type_map: &mut TypeMap) -> NamedDataType { + fn definition_named_data_type(type_map: &mut TypeCollection) -> NamedDataType { internal::construct::named_data_type( "Infallible".into(), "".into(), @@ -235,7 +235,7 @@ const _: () = { }; impl Type for std::ops::Range { - fn inline(type_map: &mut TypeMap, _generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, _generics: Generics) -> DataType { let ty = Some(T::inline(type_map, Generics::Definition)); DataType::Struct(StructType { name: "Range".into(), @@ -289,7 +289,7 @@ const _: () = { internal::construct::impl_location("specta/src/type/impls.rs:302:10"); impl Type for std::time::SystemTime { - fn inline(type_map: &mut TypeMap, _: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, _: Generics) -> DataType { DataType::Struct(internal::construct::r#struct( "SystemTime".into(), Some(internal::construct::sid("SystemTime", "::todo:3:10")), @@ -328,7 +328,7 @@ const _: () = { )) } - fn reference(type_map: &mut TypeMap, _: &[DataType]) -> reference::Reference { + fn reference(type_map: &mut TypeCollection, _: &[DataType]) -> reference::Reference { reference::reference::( type_map, internal::construct::data_type_reference("SystemTime".into(), SID, vec![]), @@ -340,7 +340,7 @@ const _: () = { fn sid() -> SpectaID { SID } - fn named_data_type(type_map: &mut TypeMap, generics: &[DataType]) -> NamedDataType { + fn named_data_type(type_map: &mut TypeCollection, generics: &[DataType]) -> NamedDataType { internal::construct::named_data_type( "SystemTime".into(), "".into(), @@ -350,7 +350,7 @@ const _: () = { ::inline(type_map, Generics::Provided(generics)), ) } - fn definition_named_data_type(type_map: &mut TypeMap) -> NamedDataType { + fn definition_named_data_type(type_map: &mut TypeCollection) -> NamedDataType { internal::construct::named_data_type( "SystemTime".into(), "".into(), @@ -371,7 +371,7 @@ const _: () = { internal::construct::impl_location("specta/src/type/impls.rs:401:10"); impl Type for std::time::Duration { - fn inline(type_map: &mut TypeMap, _: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, _: Generics) -> DataType { DataType::Struct(internal::construct::r#struct( "Duration".into(), Some(SID), @@ -409,7 +409,7 @@ const _: () = { ), )) } - fn reference(type_map: &mut TypeMap, _: &[DataType]) -> reference::Reference { + fn reference(type_map: &mut TypeCollection, _: &[DataType]) -> reference::Reference { reference::reference::( type_map, internal::construct::data_type_reference("Duration".into(), Self::sid(), vec![]), @@ -421,7 +421,7 @@ const _: () = { fn sid() -> SpectaID { SID } - fn named_data_type(type_map: &mut TypeMap, generics: &[DataType]) -> NamedDataType { + fn named_data_type(type_map: &mut TypeCollection, generics: &[DataType]) -> NamedDataType { internal::construct::named_data_type( "Duration".into(), "".into(), @@ -431,7 +431,7 @@ const _: () = { ::inline(type_map, Generics::Provided(generics)), ) } - fn definition_named_data_type(type_map: &mut TypeMap) -> NamedDataType { + fn definition_named_data_type(type_map: &mut TypeCollection) -> NamedDataType { internal::construct::named_data_type( "Duration".into(), "".into(), diff --git a/specta/src/type/legacy_impls.rs b/specta/src/type/legacy_impls.rs index 12e4d118..4ec85225 100644 --- a/specta/src/type/legacy_impls.rs +++ b/specta/src/type/legacy_impls.rs @@ -29,7 +29,7 @@ const _: () = { } impl Type for Number { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Enum(EnumType { name: "Number".into(), sid: None, @@ -111,14 +111,14 @@ const _: () = { } impl Type for serde_yaml::Mapping { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { // We don't type this more accurately because `serde_json` doesn't allow non-string map keys so neither does Specta DataType::Unknown } } impl Type for serde_yaml::value::TaggedValue { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Map(Map { key_ty: Box::new(DataType::Primitive(PrimitiveType::String)), value_ty: Box::new(DataType::Unknown), @@ -127,7 +127,7 @@ const _: () = { } impl Type for serde_yaml::Number { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Enum(EnumType { name: "Number".into(), sid: None, @@ -401,7 +401,7 @@ impl_as!(url::Url as String); #[cfg(feature = "either")] impl Type for either::Either { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { DataType::Enum(EnumType { name: "Either".into(), sid: None, @@ -447,7 +447,7 @@ impl Type for either::Either { }) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: DataType::Enum(EnumType { name: "Either".into(), diff --git a/specta/src/type/macros.rs b/specta/src/type/macros.rs index 6e82f7eb..da55cca9 100644 --- a/specta/src/type/macros.rs +++ b/specta/src/type/macros.rs @@ -1,10 +1,10 @@ macro_rules! _impl_passthrough { ($t:ty) => { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { <$t>::inline(type_map, generics) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { <$t>::reference(type_map, generics) } }; @@ -13,7 +13,7 @@ macro_rules! _impl_passthrough { macro_rules! _impl_primitives { ($($i:ident)+) => {$( impl Type for $i { - fn inline(_: &mut TypeMap, _: Generics) -> DataType { + fn inline(_: &mut TypeCollection, _: Generics) -> DataType { DataType::Primitive(datatype::PrimitiveType::$i) } } @@ -25,7 +25,7 @@ macro_rules! _impl_tuple { #[allow(non_snake_case)] impl<$($i: Type),*> Type for ($($i,)*) { #[allow(unused)] - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { let generics = match generics { Generics::Definition => &[], Generics::Provided(generics) => generics, @@ -54,7 +54,7 @@ macro_rules! _impl_tuple { macro_rules! _impl_containers { ($($container:ident)+) => {$( impl Type for $container { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { let _generics = match generics { Generics::Definition => &[], Generics::Provided(generics) => generics, @@ -67,7 +67,7 @@ macro_rules! _impl_containers { ) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: generics.get(0).cloned().unwrap_or_else( || T::reference(type_map, generics).inner, @@ -81,11 +81,11 @@ macro_rules! _impl_containers { T::sid() } - fn named_data_type(type_map: &mut TypeMap, generics: &[DataType]) -> NamedDataType { + fn named_data_type(type_map: &mut TypeCollection, generics: &[DataType]) -> NamedDataType { T::named_data_type(type_map, generics) } - fn definition_named_data_type(type_map: &mut TypeMap) -> NamedDataType { + fn definition_named_data_type(type_map: &mut TypeCollection) -> NamedDataType { T::definition_named_data_type(type_map) } } @@ -97,11 +97,11 @@ macro_rules! _impl_containers { macro_rules! _impl_as { ($($ty:path as $tty:ident)+) => {$( impl Type for $ty { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { <$tty as Type>::inline(type_map, generics) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { <$tty as Type>::reference(type_map, generics) } } @@ -111,7 +111,7 @@ macro_rules! _impl_as { macro_rules! _impl_for_list { ($($unique:expr; $ty:path as $name:expr)+) => {$( impl Type for $ty { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { let _generics = match generics { Generics::Definition => &[], Generics::Provided(generics) => generics, @@ -127,7 +127,7 @@ macro_rules! _impl_for_list { }) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: DataType::List(List { ty: Box::new(generics.get(0).cloned().unwrap_or_else( @@ -145,7 +145,7 @@ macro_rules! _impl_for_list { macro_rules! _impl_for_map { ($ty:path as $name:expr) => { impl Type for $ty { - fn inline(type_map: &mut TypeMap, generics: Generics) -> DataType { + fn inline(type_map: &mut TypeCollection, generics: Generics) -> DataType { let _generics = match generics { Generics::Definition => &[], Generics::Provided(generics) => generics, @@ -167,7 +167,7 @@ macro_rules! _impl_for_map { }) } - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + fn reference(type_map: &mut TypeCollection, generics: &[DataType]) -> Reference { Reference { inner: DataType::Map(crate::datatype::Map { key_ty: Box::new( diff --git a/specta/src/type_collection.rs b/specta/src/type_collection.rs new file mode 100644 index 00000000..ee9b5e49 --- /dev/null +++ b/specta/src/type_collection.rs @@ -0,0 +1,145 @@ +use std::{ + borrow::{Borrow, Cow}, + collections::{btree_map, BTreeMap}, + fmt, + path::Path, +}; + +use crate::{datatype::NamedDataType, Language, NamedType, SpectaID}; + +/// Define a set of types which can be exported together. +/// +/// While exporting a type will add all of the types it depends on to the collection. +/// You can also construct your own collection to easily export a set of types together. +#[derive(Clone, PartialEq)] +pub struct TypeCollection { + // `None` indicates that the entry is a placeholder. It was reference and we are currently working out it's definition. + pub(crate) map: BTreeMap>, + // A stack of types that are currently being flattened. This is used to detect cycles. + pub(crate) flatten_stack: Vec, + // A header for the exported file that can be overriden by a framework built off of `specta`. + pub(crate) header: Cow<'static, str>, +} + +impl fmt::Debug for TypeCollection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("TypeCollection").field(&self.map).finish() + } +} + +impl Default for TypeCollection { + fn default() -> Self { + Self { + map: BTreeMap::new(), + flatten_stack: Vec::new(), + header: Cow::Borrowed("// This file has been generated by Specta. DO NOT EDIT.\n"), + } + } +} + +impl TypeCollection { + /// Override the header for the exported file. + /// You should prefer setting the header using the [`Language`] instead unless your a framework. + #[doc(hidden)] // Although this is hidden it's still public API. + pub fn with_header(self, header: impl Into>) -> Self { + Self { + header: header.into(), + ..Default::default() + } + } + + /// Register a type with the collection. + pub fn register(&mut self) -> &mut Self { + let def = T::definition_named_data_type(self); + self.map.insert(T::sid(), Some(def)); + self + } + + /// Insert a type into the collection. + /// You should prefer to use `TypeCollection::register` as it ensures all invariants are met. + /// + /// When using this method it's the responsibility of the caller to: + /// - Ensure the `SpectaID` and `NamedDataType` are correctly matched. + /// - Ensure the same `TypeCollection` was used when calling `NamedType::definition_named_data_type`. + /// Not honoring these rules will result in a broken collection. + pub fn insert(&mut self, sid: SpectaID, def: NamedDataType) -> &mut Self { + self.map.insert(sid, Some(def)); + self + } + + /// Join another type collection into this one. + pub fn extend(&mut self, collection: impl Borrow) -> &mut Self { + self.map + .extend(collection.borrow().map.iter().map(|(k, v)| (*k, v.clone()))); + self + } + + /// TODO + pub fn export(&self, language: L) -> Result { + language.export(self).map(|s| format!("{}{s}", self.header)) + } + + /// TODO + pub fn export_to( + &self, + language: L, + path: impl AsRef, + ) -> Result<(), L::Error> { + let path = path.as_ref(); + std::fs::write( + &path, + language + .export(self) + .map(|s| format!("{}{s}", self.header))?, + )?; + language.format(path)?; + Ok(()) + } + + #[track_caller] + pub fn get(&self, sid: SpectaID) -> Option<&NamedDataType> { + #[allow(clippy::bind_instead_of_map)] + self.map.get(&sid).as_ref().and_then(|v| match v { + Some(ndt) => Some(ndt), + // If this method is used during type construction this case could be hit when it's actually valid + // but all references are managed within `specta` so we can bypass this method and use `map` directly because we have `pub(crate)` access. + None => { + #[cfg(debug_assertions)] + unreachable!("specta: `TypeCollection::get` found a type placeholder!"); + #[cfg(not(debug_assertions))] + None + } + }) + } +} + +impl<'a> IntoIterator for &'a TypeCollection { + type Item = (SpectaID, &'a NamedDataType); + type IntoIter = TypeCollectionInterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + TypeCollectionInterator(self.map.iter()) + } +} + +// Sealed +pub struct TypeCollectionInterator<'a>(btree_map::Iter<'a, SpectaID, Option>); + +impl<'a> ExactSizeIterator for TypeCollectionInterator<'a> {} + +impl<'a> Iterator for TypeCollectionInterator<'a> { + type Item = (SpectaID, &'a NamedDataType); + + fn next(&mut self) -> Option { + loop { + let (sid, ndt) = self.0.next()?; + if let Some(ndt) = ndt { + return Some((*sid, ndt)); + } + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.0.clone().filter(|(_, t)| t.is_none()).count())) + } +} diff --git a/specta/src/type_map.rs b/specta/src/type_map.rs deleted file mode 100644 index 5d5315a0..00000000 --- a/specta/src/type_map.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::{collections::BTreeMap, fmt}; - -use crate::{datatype::NamedDataType, SpectaID}; - -/// A map used to store the types "discovered" while exporting a type. -#[derive(Default, Clone, PartialEq)] -pub struct TypeMap { - // `None` indicates that the entry is a placeholder. It was reference and we are currently working out it's definition. - pub(crate) map: BTreeMap>, - // A stack of types that are currently being flattened. This is used to detect cycles. - pub(crate) flatten_stack: Vec, -} - -impl fmt::Debug for TypeMap { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TypeMap").field(&self.map).finish() - } -} - -impl TypeMap { - #[track_caller] - pub fn get(&self, sid: SpectaID) -> Option<&NamedDataType> { - #[allow(clippy::bind_instead_of_map)] - self.map.get(&sid).as_ref().and_then(|v| match v { - Some(ndt) => Some(ndt), - // If this method is used during type construction this case could be hit when it's actually valid - // but all references are managed within `specta` so we can bypass this method and use `map` directly because we have `pub(crate)` access. - None => { - #[cfg(debug_assertions)] - unreachable!("specta: `TypeMap::get` found a type placeholder!"); - #[cfg(not(debug_assertions))] - None - } - }) - } - - pub fn insert(&mut self, sid: SpectaID, dt: NamedDataType) { - self.map.insert(sid, Some(dt)); - } - - pub fn is_empty(&self) -> bool { - self.map.is_empty() - } - - pub fn len(&self) -> usize { - self.map.len() - } - - pub fn contains_key(&self, sid: SpectaID) -> bool { - self.map.contains_key(&sid) - } - - pub fn remove(&mut self, sid: SpectaID) -> Option { - self.map.remove(&sid).flatten() - } - - pub fn append(&mut self, type_map: &mut TypeMap) { - self.map.append(&mut type_map.map); - } - - // TODO: It would be nice if this would a proper `Iterator` or `IntoIterator` implementation! - pub fn iter(&self) -> impl Iterator { - #[allow(clippy::unnecessary_filter_map)] - self.map.iter().filter_map(|(sid, ndt)| match ndt { - Some(ndt) => Some((*sid, ndt)), - None => { - #[cfg(debug_assertions)] - unreachable!("specta: `TypeMap::into_iter` found a type placeholder!"); - #[cfg(not(debug_assertions))] - None - } - }) - } -} diff --git a/tests/tests/functions.rs b/tests/tests/functions.rs index bad69a37..7d520cb2 100644 --- a/tests/tests/functions.rs +++ b/tests/tests/functions.rs @@ -109,7 +109,7 @@ mod test { #[test] fn test_function_exporting() { { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![a](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "a"); @@ -118,7 +118,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![b](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "b"); @@ -132,7 +132,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![c](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "c"); @@ -156,7 +156,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![d](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "d"); @@ -184,7 +184,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![e::](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "e"); @@ -198,7 +198,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![f](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "f"); @@ -226,7 +226,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![g](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "g"); @@ -240,7 +240,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![h](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "h"); @@ -254,7 +254,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![i](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "i"); @@ -277,7 +277,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![k](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "k"); @@ -300,7 +300,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![l](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "l"); @@ -319,7 +319,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![m](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "m"); @@ -333,7 +333,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![async_fn](&type_map); assert_eq!(def.asyncness, true); assert_eq!(def.name, "async_fn"); @@ -342,7 +342,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![with_docs](&type_map); assert_eq!(def.asyncness, false); assert_eq!(def.name, "with_docs"); @@ -352,7 +352,7 @@ mod test { } { - let mut type_map = &mut specta::TypeMap::default(); + let mut type_map = &mut specta::TypeCollection::default(); let def: datatype::Function = specta::fn_datatype![raw](&type_map); assert_eq!(def.args[0].0, "type"); } diff --git a/tests/tests/sid.rs b/tests/tests/sid.rs index e421a7cb..45bd3dc2 100644 --- a/tests/tests/sid.rs +++ b/tests/tests/sid.rs @@ -1,4 +1,4 @@ -use specta::{Generics, Type, TypeMap}; +use specta::{Generics, Type, TypeCollection}; #[derive(Type)] #[specta(export = false)] @@ -29,11 +29,11 @@ pub struct BagOfTypes { #[test] fn test_sid() { // TODO: This is so hard for an end-user to work with. Add some convenience API's!!! - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); // We are calling this for it's side-effects BagOfTypes::inline(&mut type_map, Generics::Definition); - // `TypeMap` is a `BTreeMap` so it's sorted by SID. It should be sorted alphabetically by name + // `TypeCollection` is a `BTreeMap` so it's sorted by SID. It should be sorted alphabetically by name assert_eq!( type_map .iter() diff --git a/tests/tests/type_collection.rs b/tests/tests/type_collection.rs index a7cd873a..4841a70d 100644 --- a/tests/tests/type_collection.rs +++ b/tests/tests/type_collection.rs @@ -1,4 +1,4 @@ -use specta::{Type, TypeMap}; +use specta::{Type, TypeCollection}; use specta_util::TypeCollection; #[derive(Type)] @@ -19,7 +19,7 @@ struct D(String); #[test] fn type_collection_export() { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); TypeCollection::default() .register::() .collect(&mut type_map); @@ -33,7 +33,7 @@ fn type_collection_merge() { let mut b = TypeCollection::default(); b.register::(); - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); TypeCollection::default() .register::() .extend(a) @@ -50,7 +50,7 @@ fn type_collection_merge() { #[test] fn type_collection_duplicate_register_ty() { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); TypeCollection::default() .register::() .register::() diff --git a/tests/tests/type_map.rs b/tests/tests/type_map.rs index bcc4e955..6606b152 100644 --- a/tests/tests/type_map.rs +++ b/tests/tests/type_map.rs @@ -1,4 +1,4 @@ -use specta::{Generics, Type, TypeMap}; +use specta::{Generics, Type, TypeCollection}; use specta_typescript as ts; #[derive(Type)] @@ -15,7 +15,7 @@ pub struct ActualType { #[test] fn test_generic_type_in_type_map() { - let mut type_map = TypeMap::default(); + let mut type_map = TypeCollection::default(); ActualType::inline(&mut type_map, Generics::NONE); assert_eq!(type_map.len(), 1); diff --git a/tests/tests/typescript/export_function_header.rs b/tests/tests/typescript/export_function_header.rs index 5362ea99..9c32f851 100644 --- a/tests/tests/typescript/export_function_header.rs +++ b/tests/tests/typescript/export_function_header.rs @@ -1,6 +1,6 @@ // #![allow(deprecated)] -// use specta::{datatype::Function, function::fn_datatype, specta, TypeMap}; +// use specta::{datatype::Function, function::fn_datatype, specta, TypeCollection}; // #[specta] // fn a() {} @@ -23,31 +23,31 @@ // #[test] // fn test_export_function_header() { // assert( -// fn_datatype!(a)(&mut TypeMap::default()), +// fn_datatype!(a)(&mut TypeCollection::default()), // Ok("export function a();"), // ); // assert( -// fn_datatype!(b)(&mut TypeMap::default()), +// fn_datatype!(b)(&mut TypeCollection::default()), // Ok("export function b(): null;"), // ); // assert( -// fn_datatype!(c)(&mut TypeMap::default()), +// fn_datatype!(c)(&mut TypeCollection::default()), // Ok("export async function c();"), // ); // assert( -// fn_datatype!(d)(&mut TypeMap::default()), +// fn_datatype!(d)(&mut TypeCollection::default()), // Ok("export function d(): string;"), // ); // assert( -// fn_datatype!(e)(&mut TypeMap::default()), +// fn_datatype!(e)(&mut TypeCollection::default()), // Ok("export function e(a: string);"), // ); // assert( -// fn_datatype!(f)(&mut TypeMap::default()), +// fn_datatype!(f)(&mut TypeCollection::default()), // Ok("export function f(a: string, b: number);"), // ); // assert( -// fn_datatype!(g)(&mut TypeMap::default()), +// fn_datatype!(g)(&mut TypeCollection::default()), // Ok("/**\n * @deprecated\n */\nexport function g();"), // ); // }