From 2e1cbf29119148e4a0735a088327e0f8ff683588 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Sat, 2 Sep 2023 18:40:58 +0800 Subject: [PATCH] Drop `NamedDataTypeItem::Tuple` --- macros/src/data_type_from/mod.rs | 22 +++++++--- macros/src/type/enum.rs | 14 +++---- macros/src/type/struct.rs | 22 +++++----- src/datatype/enum.rs | 47 ++++++++++++++------- src/datatype/mod.rs | 12 +++++- src/datatype/named.rs | 62 +--------------------------- src/datatype/struct.rs | 39 +++++++++++++++--- src/datatype/tuple.rs | 9 +--- src/internal.rs | 35 ++++++++++++---- src/lang/ts/export_config.rs | 8 +++- src/lang/ts/mod.rs | 71 ++++++++++++++------------------ src/type/impls.rs | 16 +++---- src/type/macros.rs | 1 - tests/datatype.rs | 29 ++++++++++++- 14 files changed, 217 insertions(+), 170 deletions(-) diff --git a/macros/src/data_type_from/mod.rs b/macros/src/data_type_from/mod.rs index 067b4f5..9625b0e 100644 --- a/macros/src/data_type_from/mod.rs +++ b/macros/src/data_type_from/mod.rs @@ -9,7 +9,11 @@ use crate::utils::parse_attrs; pub fn derive(input: proc_macro::TokenStream) -> syn::Result { let DeriveInput { - ident, data, attrs, .. + ident, + data, + attrs, + generics, + .. } = &parse_macro_input::parse::(input)?; let mut attrs = parse_attrs(attrs)?; @@ -17,6 +21,13 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result 0 { + return Err(syn::Error::new_spanned( + generics, + "DataTypeFrom does not support generics", + )); + } + Ok(match data { Data::Struct(data) => match &data.fields { Fields::Named(_) => { @@ -63,7 +74,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result for #crate_ref::DataType { fn from(t: #ident) -> #crate_ref::DataType { - #crate_ref::DataType::Struct(t.into()) + Self::Struct(t.into()) } } } @@ -72,14 +83,13 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result>(); quote! { #[automatically_derived] impl From<#ident> for #crate_ref::TupleType { fn from(t: #ident) -> #crate_ref::TupleType { - #crate_ref::internal::construct::tuple_type( - vec![], + #crate_ref::internal::construct::tuple( vec![#(#fields),*] ) } @@ -88,7 +98,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result for #crate_ref::DataType { fn from(t: #ident) -> #crate_ref::DataType { - #crate_ref::DataType::Tuple(t.into()) + Self::Tuple(t.into()) } } } diff --git a/macros/src/type/enum.rs b/macros/src/type/enum.rs index 7b48ba7..57c739e 100644 --- a/macros/src/type/enum.rs +++ b/macros/src/type/enum.rs @@ -116,7 +116,7 @@ pub fn parse_enum( }) .collect::>>()?; - quote!(#crate_ref::EnumVariant::Unnamed(#crate_ref::internal::construct::tuple_type( + quote!(#crate_ref::EnumVariant::Unnamed(#crate_ref::internal::construct::unnamed_struct_fields( vec![], vec![#(#fields.into()),*], ))) @@ -163,7 +163,7 @@ pub fn parse_enum( }) .collect::>>()?; - quote!(#crate_ref::EnumVariant::Named(#crate_ref::internal::construct::named_struct(vec![], vec![#(#fields),*], None))) + quote!(#crate_ref::EnumVariant::Named(#crate_ref::internal::construct::named_struct_fields(vec![], vec![#(#fields),*], None))) } }, )) @@ -175,7 +175,7 @@ pub fn parse_enum( let (enum_impl, can_flatten) = match repr { Tagged::Untagged => ( quote! { - #crate_ref::internal::construct::untagged_enum(vec![#(#variant_types),*], vec![#(#definition_generics),*]) + #crate_ref::internal::construct::untagged_enum(vec![#(#definition_generics),*], vec![#(#variant_types),*]) }, data.variants .iter() @@ -184,8 +184,8 @@ pub fn parse_enum( Tagged::Externally => ( quote! { #crate_ref::internal::construct::tagged_enum( - vec![#((#variant_names.into(), #variant_types)),*], vec![#(#definition_generics),*], + vec![#((#variant_names.into(), #variant_types)),*], #crate_ref::EnumRepr::External ) }, @@ -198,8 +198,8 @@ pub fn parse_enum( Tagged::Adjacently { tag, content } => ( quote! { #crate_ref::internal::construct::tagged_enum( - vec![#((#variant_names.into(), #variant_types)),*], vec![#(#definition_generics),*], + vec![#((#variant_names.into(), #variant_types)),*], #crate_ref::EnumRepr::Adjacent { tag: #tag.into(), content: #content.into() } ) }, @@ -208,8 +208,8 @@ pub fn parse_enum( Tagged::Internally { tag } => ( quote! { #crate_ref::internal::construct::tagged_enum( - vec![#((#variant_names.into(), #variant_types)),*], vec![#(#definition_generics),*], + vec![#((#variant_names.into(), #variant_types)),*], #crate_ref::EnumRepr::Internal { tag: #tag.into() }, ) }, @@ -224,7 +224,7 @@ pub fn parse_enum( container_attrs, name, quote! { - #crate_ref::NamedDataTypeItem::Enum( + #crate_ref::DataType::Enum( #enum_impl ) }, diff --git a/macros/src/type/struct.rs b/macros/src/type/struct.rs index 2b2334a..53c13a6 100644 --- a/macros/src/type/struct.rs +++ b/macros/src/type/struct.rs @@ -127,8 +127,10 @@ pub fn parse_struct( #crate_ref::DataType::Enum(item) => { item.make_flattenable(IMPL_LOCATION)?; } - #crate_ref::DataType::Named(#crate_ref::NamedDataType { item: #crate_ref::NamedDataTypeItem::Enum(item), .. }) => { - item.make_flattenable(IMPL_LOCATION)?; + #crate_ref::DataType::Named(#crate_ref::NamedDataType { item, .. }) => { + if let #crate_ref::DataType::Enum(item) = &mut **item { + item.make_flattenable(IMPL_LOCATION)?; + } } _ => {} } @@ -164,7 +166,7 @@ pub fn parse_struct( container_attrs, name, quote! { - #crate_ref::NamedDataTypeItem::Struct(#crate_ref::internal::construct::named_struct(vec![#(#definition_generics),*], vec![#(#fields),*], #tag)) + #crate_ref::DataType::Struct(#crate_ref::internal::construct::named_struct(vec![#(#definition_generics),*], vec![#(#fields),*], #tag)) }, ) } @@ -196,7 +198,7 @@ pub fn parse_struct( )?; quote! { - #crate_ref::NamedDataTypeItem::Struct(#crate_ref::internal::construct::unnamed_struct( + #crate_ref::DataType::Struct(#crate_ref::internal::construct::unnamed_struct( vec![#(#definition_generics),*], vec![ { @@ -242,12 +244,10 @@ pub fn parse_struct( .collect::>>()?; quote! { - #crate_ref::NamedDataTypeItem::Tuple( - #crate_ref::internal::construct::tuple_type( - vec![#(#definition_generics),*], - vec![#(#fields),*], - ) - ) + #crate_ref::DataType::Struct(#crate_ref::internal::construct::unnamed_struct( + vec![#(#definition_generics),*], + vec![#(#fields),*], + )) } } }; @@ -259,7 +259,7 @@ pub fn parse_struct( container_attrs, name, quote! { - #crate_ref::NamedDataTypeItem::Struct(#crate_ref::internal::construct::unit_struct()) + #crate_ref::DataType::Struct(#crate_ref::internal::construct::unit_struct()) }, ), }; diff --git a/src/datatype/enum.rs b/src/datatype/enum.rs index 79e0005..e46ec0c 100644 --- a/src/datatype/enum.rs +++ b/src/datatype/enum.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use crate::{ - datatype::{DataType, StructType, TupleType}, - ExportError, GenericType, ImplLocation, + datatype::DataType, ExportError, GenericType, ImplLocation, NamedDataType, StructNamedFields, + StructUnnamedFields, }; #[derive(Debug, Clone, PartialEq)] @@ -66,14 +66,26 @@ pub enum EnumType { Tagged(TaggedEnum), } -impl From for DataType { - fn from(t: EnumType) -> Self { - Self::Enum(t) +impl EnumType { + /// Convert a [`EnumType`] to an anonymous [`DataType`]. + pub fn to_anonymous(self) -> DataType { + DataType::Enum(self) } -} -impl EnumType { - pub(crate) fn generics(&self) -> &Vec { + /// Convert a [`EnumType`] to a named [`NamedDataType`]. + /// + /// This can easily be converted to a [`DataType`] by putting it inside the [DataType::Named] variant. + pub fn to_named(self, name: impl Into>) -> NamedDataType { + NamedDataType { + name: name.into(), + comments: vec![], + deprecated: None, + ext: None, + item: Box::new(DataType::Enum(self)), + } + } + + pub fn generics(&self) -> &Vec { match self { Self::Untagged(UntaggedEnum { generics, .. }) => generics, Self::Tagged(TaggedEnum { generics, .. }) => generics, @@ -110,8 +122,8 @@ impl EnumType { "`EnumRepr::External` with ` EnumVariant::Unit` is invalid!", )), EnumVariant::Unnamed(v) => match v { - TupleType { fields, .. } if fields.len() == 1 => Ok(()), - TupleType { .. } => Err(ExportError::InvalidType( + StructUnnamedFields { fields, .. } if fields.len() == 1 => Ok(()), + StructUnnamedFields { .. } => Err(ExportError::InvalidType( impl_location, "`EnumRepr::External` with `EnumVariant::Unnamed` containing more than a single field is invalid!", )), @@ -136,6 +148,12 @@ impl EnumType { } } +impl From for DataType { + fn from(t: EnumType) -> Self { + Self::Enum(t) + } +} + /// Serde representation of an enum. /// /// Does not contain [`Untagged`](EnumType::Untagged) as that is handled by [`EnumType`]. @@ -157,8 +175,9 @@ pub enum EnumRepr { #[allow(missing_docs)] pub enum EnumVariant { Unit, - Named(StructType), - Unnamed(TupleType), + // TODO: Should these be holding the `struct` types or have their own??? + Named(StructNamedFields), + Unnamed(StructUnnamedFields), } impl EnumVariant { @@ -166,8 +185,8 @@ impl EnumVariant { pub fn data_type(&self) -> DataType { match self { Self::Unit => unreachable!("Unit enum variants have no type!"), // TODO: Remove unreachable in type system + avoid following clones - Self::Unnamed(tuple_type) => tuple_type.clone().into(), - Self::Named(object_type) => object_type.clone().into(), + Self::Unnamed(tuple_type) => DataType::Struct(tuple_type.clone().into()), + Self::Named(object_type) => DataType::Struct(object_type.clone().into()), } } } diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index 800d74e..679fbb1 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -60,6 +60,16 @@ pub enum DataType { Generic(GenericType), } +impl DataType { + pub fn generics(&self) -> Option> { + match self { + Self::Struct(s) => Some(s.generics()), + Self::Enum(e) => Some(e.generics().clone()), // TODO: Cringe clone + _ => None, + } + } +} + /// A reference to a [`DataType`] that can be used before a type is resolved in order to /// support recursive types without causing an infinite loop. /// @@ -123,7 +133,7 @@ impl + 'static> From> for DataType { variants: t .into_iter() .map(|t| { - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![t.into()], generics: vec![], }) diff --git a/src/datatype/named.rs b/src/datatype/named.rs index 834496d..3b98776 100644 --- a/src/datatype/named.rs +++ b/src/datatype/named.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::{DataType, EnumType, GenericType, ImplLocation, SpectaID, StructType, TupleType}; +use crate::{DataType, ImplLocation, SpectaID}; /// A NamedDataTypeImpl includes extra information which is only available for [NamedDataType]'s that come from a real Rust type. #[derive(Debug, Clone, PartialEq)] @@ -43,7 +43,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 fixed when reviewing the flattening logic/error handling - pub item: NamedDataTypeItem, + pub item: Box, } impl NamedDataType { @@ -69,61 +69,3 @@ impl From for DataType { Self::Named(t) } } - -/// The possible types for a [`NamedDataType`]. -/// -/// This type will model the type of the Rust type that is being exported but be aware of the following: -/// ```rust -/// #[derive(serde::Serialize)] -/// struct Demo {} -/// // is: NamedDataTypeItem::Struct -/// // typescript: `{}` -/// -/// #[derive(serde::Serialize)] -/// struct Demo2(); -/// // is: NamedDataTypeItem::Tuple(TupleType::Unnamed) // TODO Fix this -/// // typescript: `[]` -/// -/// #[derive(specta::Type)] -/// struct Demo3; -///// is: NamedDataTypeItem::Tuple(TupleType(_)) -/// // typescript: `null` -/// ``` -#[derive(Debug, Clone, PartialEq)] -pub enum NamedDataTypeItem { - /// Represents an Rust struct with named fields - Struct(StructType), - /// Represents an Rust enum - Enum(EnumType), - /// Represents an Rust struct with unnamed fields - Tuple(TupleType), -} - -impl NamedDataTypeItem { - /// Converts a [`NamedDataTypeItem`] into a [`DataType`] - pub fn datatype(self) -> DataType { - match self { - Self::Struct(o) => o.into(), - Self::Enum(e) => e.into(), - Self::Tuple(t) => t.into(), - } - } - - /// Returns the generics arguments for the type - pub fn generics(&self) -> Vec { - match self { - // Named struct - Self::Struct(s) => match s { - StructType::Unit => vec![], - StructType::Unnamed(s) => s.generics.clone(), - StructType::Named(s) => s.generics.clone(), - }, - // Enum - Self::Enum(e) => e.generics().clone(), - // Struct with unnamed fields - Self::Tuple(tuple) => match tuple { - TupleType { generics, .. } => generics.clone(), - }, - } - } -} diff --git a/src/datatype/struct.rs b/src/datatype/struct.rs index 27d3814..dd97ee9 100644 --- a/src/datatype/struct.rs +++ b/src/datatype/struct.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::{DataType, GenericType, NamedDataType, NamedDataTypeItem, TupleType}; +use crate::{DataType, GenericType, NamedDataType}; /// A field in an [`StructType`]. #[derive(Debug, Clone, PartialEq)] @@ -29,10 +29,33 @@ impl StructField { } } +#[derive(Debug, Clone, PartialEq)] +pub struct StructUnnamedFields { + pub(crate) fields: Vec, // TODO: should use `StructField` but without `name` for flatten/inline + pub(crate) generics: Vec, +} + +impl StructUnnamedFields { + pub fn generics(&self) -> impl Iterator { + self.generics.iter() + } + + // TODO: Make this work + // pub fn fields(&self) -> impl Iterator { + // self.fields.iter() + // } +} + +impl Into for StructUnnamedFields { + fn into(self) -> StructType { + StructType::Unnamed(self) + } +} + #[derive(Debug, Clone, PartialEq)] pub struct StructNamedFields { pub(crate) generics: Vec, - pub(crate) fields: Vec, + pub(crate) fields: Vec, // TODO: StructField but with name pub(crate) tag: Option>, } @@ -50,6 +73,12 @@ impl StructNamedFields { } } +impl Into for StructNamedFields { + fn into(self) -> StructType { + StructType::Named(self) + } +} + #[derive(Debug, Clone, PartialEq)] pub enum StructType { /// A unit struct. @@ -59,10 +88,10 @@ pub enum StructType { /// A struct with unnamed fields. /// /// Represented in Rust as `pub struct Unit();` and in TypeScript as `[]`. - Unnamed(TupleType), + Unnamed(StructUnnamedFields), /// A struct with named fields. /// - /// Represented in Rust as `pub struct Unit{};` and in TypeScript as `{}`. + /// Represented in Rust as `pub struct Unit {}` and in TypeScript as `{}`. Named(StructNamedFields), } @@ -81,7 +110,7 @@ impl StructType { comments: vec![], deprecated: None, ext: None, - item: NamedDataTypeItem::Struct(self), + item: Box::new(DataType::Struct(self)), } } diff --git a/src/datatype/tuple.rs b/src/datatype/tuple.rs index b1fe194..7f7833b 100644 --- a/src/datatype/tuple.rs +++ b/src/datatype/tuple.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::{DataType, GenericType, NamedDataType, NamedDataTypeItem}; +use crate::{DataType, NamedDataType}; /// A regular tuple /// @@ -9,7 +9,6 @@ use crate::{DataType, GenericType, NamedDataType, NamedDataTypeItem}; #[derive(Debug, Clone, PartialEq)] pub struct TupleType { pub(crate) fields: Vec, - pub(crate) generics: Vec, } impl TupleType { @@ -27,17 +26,13 @@ impl TupleType { comments: vec![], deprecated: None, ext: None, - item: NamedDataTypeItem::Tuple(self), + item: Box::new(DataType::Tuple(self)), } } pub fn fields(&self) -> impl Iterator { self.fields.iter() } - - pub fn generics(&self) -> impl Iterator { - self.generics.iter() - } } impl From for DataType { diff --git a/src/internal.rs b/src/internal.rs index 12fb1f1..8e03106 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -21,11 +21,29 @@ pub mod construct { StructType::Unit } + pub const fn unnamed_struct_fields( + generics: Vec, + fields: Vec, + ) -> StructUnnamedFields { + StructUnnamedFields { generics, fields } + } + // TODO: By taking in `DataType` how does `flatten` and `inline` work pub const fn unnamed_struct(generics: Vec, fields: Vec) -> StructType { - StructType::Unnamed(TupleType { generics, fields }) + StructType::Unnamed(StructUnnamedFields { generics, fields }) } + pub const fn named_struct_fields( + generics: Vec, + fields: Vec, + tag: Option>, + ) -> StructNamedFields { + StructNamedFields { + generics, + fields, + tag, + } + } pub const fn named_struct( generics: Vec, fields: Vec, @@ -52,14 +70,15 @@ pub mod construct { } } - pub const fn named_data_type( + // TODO: `const` + pub fn named_data_type( name: Cow<'static, str>, comments: Vec>, deprecated: Option>, sid: SpectaID, impl_location: ImplLocation, export: Option, - item: NamedDataTypeItem, + item: DataType, ) -> NamedDataType { NamedDataType { name, @@ -70,7 +89,7 @@ pub mod construct { impl_location, export, }), - item, + item: Box::new(item), } } @@ -86,13 +105,13 @@ pub mod construct { } } - pub const fn untagged_enum(variants: Vec, generics: Vec) -> EnumType { + pub const fn untagged_enum(generics: Vec, variants: Vec) -> EnumType { EnumType::Untagged(UntaggedEnum { variants, generics }) } pub const fn tagged_enum( - variants: Vec<(Cow<'static, str>, EnumVariant)>, generics: Vec, + variants: Vec<(Cow<'static, str>, EnumVariant)>, repr: EnumRepr, ) -> EnumType { EnumType::Tagged(TaggedEnum { @@ -102,7 +121,7 @@ pub mod construct { }) } - pub const fn tuple_type(generics: Vec, fields: Vec) -> TupleType { - TupleType { fields, generics } + pub const fn tuple(fields: Vec) -> TupleType { + TupleType { fields } } } diff --git a/src/lang/ts/export_config.rs b/src/lang/ts/export_config.rs index feec0fc..e10775d 100644 --- a/src/lang/ts/export_config.rs +++ b/src/lang/ts/export_config.rs @@ -39,8 +39,12 @@ impl ExportConfig { /// /// Implementations: /// - [`js_doc`](crate::lang::ts::js_doc) - pub fn comment_style(mut self, exporter: CommentFormatterFn) -> Self { - self.comment_exporter = Some(exporter); + /// + /// Not calling this method will default to the [`js_doc`](crate::lang::ts::js_doc) exporter. + /// `None` will disable comment exporting. + /// `Some(exporter)` will enable comment exporting using the provided exporter. + pub fn comment_style(mut self, exporter: Option) -> Self { + self.comment_exporter = exporter; self } diff --git a/src/lang/ts/mod.rs b/src/lang/ts/mod.rs index e9cd8a2..feb1450 100644 --- a/src/lang/ts/mod.rs +++ b/src/lang/ts/mod.rs @@ -102,9 +102,10 @@ fn export_datatype_inner( let ctx = ctx.with(PathItem::Type(name.clone())); let name = sanitise_type_name(ctx.clone(), NamedLocation::Type, name)?; - let inline_ts = named_datatype_inner(ctx.clone(), typ, type_map)?; + let inline_ts = datatype_inner(ctx.clone(), &typ.item, type_map, "null")?; - let generics = Some(item.generics()) + let generics = item + .generics() .filter(|generics| !generics.is_empty()) .map(|generics| format!("<{}>", generics.join(", "))) .unwrap_or_default(); @@ -120,30 +121,6 @@ fn export_datatype_inner( )) } -/// Convert a NamedDataType to a TypeScript string -/// -/// Eg. `{ scalar_field: number, generc_field: T }` -pub fn named_datatype(conf: &ExportConfig, typ: &NamedDataType, type_map: &TypeMap) -> Output { - named_datatype_inner( - ExportContext { - conf, - path: vec![PathItem::Type(typ.name.clone())], - }, - typ, - type_map, - ) -} - -fn named_datatype_inner(ctx: ExportContext, typ: &NamedDataType, type_map: &TypeMap) -> Output { - let name = Some(&typ.name); - - match &typ.item { - NamedDataTypeItem::Struct(o) => struct_datatype(ctx, name, o, type_map), - NamedDataTypeItem::Enum(e) => enum_datatype(ctx, name, e, type_map), - NamedDataTypeItem::Tuple(t) => tuple_datatype(ctx, t, type_map, "[]"), - } -} - /// Convert a DataType to a TypeScript string /// /// Eg. `{ demo: string; }` @@ -193,7 +170,7 @@ fn datatype_inner( DataType::Map(def) => { let is_enum = match &def.0 { DataType::Enum(_) => true, - DataType::Named(dt) => matches!(dt.item, NamedDataTypeItem::Enum(_)), + DataType::Named(dt) => matches!(*dt.item, DataType::Enum(_)), DataType::Reference(r) => { let typ = type_map .get(&r.sid()) @@ -201,7 +178,7 @@ fn datatype_inner( .as_ref() .unwrap_or_else(|| panic!("Type {} has no value!", r.name())); - matches!(typ.item, NamedDataTypeItem::Enum(_)) + matches!(*typ.item, DataType::Enum(_)) } _ => false, }; @@ -227,9 +204,12 @@ fn datatype_inner( DataType::Struct(item) => struct_datatype(ctx, None, item, type_map)?, DataType::Enum(item) => enum_datatype(ctx, None, item, type_map)?, DataType::Tuple(tuple) => tuple_datatype(ctx, tuple, type_map, empty_tuple_fallback)?, - DataType::Named(typ) => { - named_datatype_inner(ctx.with(PathItem::Type(typ.name.clone())), typ, type_map)? - } + DataType::Named(typ) => datatype_inner( + ctx.with(PathItem::Type(typ.name.clone())), + &typ.item, + type_map, + "null", + )?, DataType::Result(result) => { let mut variants = vec![ datatype_inner(ctx.clone(), &result.0, type_map, "null")?, @@ -261,6 +241,25 @@ fn datatype_inner( }) } +fn struct_unnamed_datatype( + ctx: ExportContext, + StructUnnamedFields { fields, .. }: &StructUnnamedFields, + type_map: &TypeMap, + empty_tuple_fallback: &'static str, +) -> Output { + match &fields[..] { + [] => Ok(empty_tuple_fallback.to_string()), + [ty] => datatype_inner(ctx, ty, type_map, "null"), + tys => Ok(format!( + "[{}]", + tys.iter() + .map(|v| datatype_inner(ctx.clone(), v, type_map, "null")) + .collect::>>()? + .join(", ") + )), + } +} + fn tuple_datatype( ctx: ExportContext, tuple: &TupleType, @@ -290,7 +289,7 @@ fn struct_datatype( ) -> Output { match s { StructType::Unit => return Ok("null".into()), - StructType::Unnamed(tuple) => tuple_datatype(ctx, tuple, type_map, "[]"), + StructType::Unnamed(s) => struct_unnamed_datatype(ctx, s, type_map, "[]"), StructType::Named(s) => { if s.fields.len() == 0 { return Ok("Record".into()); @@ -357,12 +356,7 @@ fn enum_datatype( format!("{{ {tag}: {sanitised_name} }}") } (EnumRepr::Internal { tag }, EnumVariant::Unnamed(tuple)) => { - let typ = datatype_inner( - ctx, - &DataType::Tuple(tuple.clone()), - type_map, - "[]", - )?; + let typ = struct_unnamed_datatype(ctx, &tuple, type_map, "[]")?; format!("({{ {tag}: {sanitised_name} }} & {typ})") } (EnumRepr::Internal { tag }, EnumVariant::Named(obj)) => { @@ -370,7 +364,6 @@ fn enum_datatype( fields.extend( obj.fields() - .iter() .map(|v| { object_field_to_ts( ctx.with(PathItem::Field(v.key.clone())), diff --git a/src/type/impls.rs b/src/type/impls.rs index fbcc024..d04b7d4 100644 --- a/src/type/impls.rs +++ b/src/type/impls.rs @@ -258,15 +258,15 @@ const _: () = { Ok(DataType::Enum( UntaggedEnum { variants: vec![ - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![DataType::Primitive(PrimitiveType::f64)], generics: vec![], }), - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![DataType::Primitive(PrimitiveType::i64)], generics: vec![], }), - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![DataType::Primitive(PrimitiveType::u64)], generics: vec![], }), @@ -304,15 +304,15 @@ const _: () = { Ok(DataType::Enum( UntaggedEnum { variants: vec![ - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![DataType::Primitive(PrimitiveType::f64)], generics: vec![], }), - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![DataType::Primitive(PrimitiveType::i64)], generics: vec![], }), - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![DataType::Primitive(PrimitiveType::u64)], generics: vec![], }), @@ -513,7 +513,7 @@ impl Type for either::Either { fn inline(opts: DefOpts, generics: &[DataType]) -> Result { Ok(DataType::Enum(EnumType::Untagged(UntaggedEnum { variants: vec![ - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![L::inline( DefOpts { parent_inline: opts.parent_inline, @@ -523,7 +523,7 @@ impl Type for either::Either { )?], generics: vec![], }), - EnumVariant::Unnamed(TupleType { + EnumVariant::Unnamed(StructUnnamedFields { fields: vec![R::inline( DefOpts { parent_inline: opts.parent_inline, diff --git a/src/type/macros.rs b/src/type/macros.rs index e01b784..e10e9ce 100644 --- a/src/type/macros.rs +++ b/src/type/macros.rs @@ -34,7 +34,6 @@ macro_rules! impl_tuple { Ok(datatype::TupleType { fields: vec![$($i),*], - generics: vec![] }.to_anonymous()) } } diff --git a/tests/datatype.rs b/tests/datatype.rs index 555fe52..4173870 100644 --- a/tests/datatype.rs +++ b/tests/datatype.rs @@ -1,4 +1,4 @@ -use specta::{ts, DataType, DataTypeFrom, LiteralType, StructType, TupleType}; +use specta::{ts, DataType, DataTypeFrom, EnumType, LiteralType, StructType, TupleType}; use crate::ts::assert_ts; @@ -29,6 +29,12 @@ struct Procedures4 { #[derive(DataTypeFrom)] struct Procedures5(Vec); +#[derive(DataTypeFrom)] +struct Procedures7(); + +#[derive(DataTypeFrom)] +struct Procedures8 {} + #[test] fn test_datatype() { let val: TupleType = Procedures1(vec![ @@ -135,4 +141,25 @@ fn test_datatype() { ), Ok("{ queries: \"A\" | \"B\" }".into()) ); + + // TODO: Fix this test + // let val: TupleType = Procedures7().into(); + // assert_eq!( + // ts::datatype( + // &Default::default(), + // &val.clone().to_anonymous(), + // &Default::default() + // ), + // Ok("[]".into()) + // ); + + let val: StructType = Procedures8 {}.into(); + assert_eq!( + ts::datatype( + &Default::default(), + &val.clone().to_anonymous(), + &Default::default() + ), + Ok("Record".into()) + ); }