Skip to content

Commit

Permalink
fixes #180
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Nov 23, 2023
1 parent 27d52f9 commit 72c8ee7
Show file tree
Hide file tree
Showing 14 changed files with 72 additions and 18 deletions.
2 changes: 1 addition & 1 deletion macros/src/data_type_from/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenSt
#[automatically_derived]
impl From<#ident> for #crate_ref::StructType {
fn from(t: #ident) -> #crate_ref::StructType {
#crate_ref::internal::construct::r#struct(#struct_name.into(), vec![], #crate_ref::internal::construct::struct_named(vec![#(#fields),*], None))
#crate_ref::internal::construct::r#struct(#struct_name.into(), None, vec![], #crate_ref::internal::construct::struct_named(vec![#(#fields),*], None))
}
}

Expand Down
2 changes: 1 addition & 1 deletion macros/src/type/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ pub fn parse_enum(
let skip_bigint_checs = enum_attrs.unstable_skip_bigint_checks;

Ok((
quote!(#crate_ref::DataType::Enum(#crate_ref::internal::construct::r#enum(#name.into(), #repr, #skip_bigint_checs, vec![#(#definition_generics),*], vec![#(#variant_types),*]))),
quote!(#crate_ref::DataType::Enum(#crate_ref::internal::construct::r#enum(#name.into(), SID, #repr, #skip_bigint_checs, vec![#(#definition_generics),*], vec![#(#variant_types),*]))),
quote!({
let generics = vec![#(#reference_generics),*];
#crate_ref::reference::reference::<Self>(opts, #crate_ref::internal::construct::data_type_reference(
Expand Down
2 changes: 1 addition & 1 deletion macros/src/type/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ pub fn parse_struct(
Fields::Unit => quote!(#crate_ref::internal::construct::struct_unit()),
};

quote!(#crate_ref::DataType::Struct(#crate_ref::internal::construct::r#struct(#name.into(), vec![#(#definition_generics),*], #fields)))
quote!(#crate_ref::DataType::Struct(#crate_ref::internal::construct::r#struct(#name.into(), Some(SID), vec![#(#definition_generics),*], #fields)))
};

let category = if container_attrs.inline {
Expand Down
7 changes: 6 additions & 1 deletion src/datatype/enum.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::borrow::Cow;

use crate::{
datatype::DataType, DeprecatedType, GenericType, NamedDataType, NamedFields, UnnamedFields,
datatype::DataType, DeprecatedType, GenericType, NamedDataType, NamedFields, SpectaID,
UnnamedFields,
};

/// Enum type which dictates how the enum is represented.
Expand All @@ -13,6 +14,10 @@ use crate::{
#[derive(Debug, Clone, PartialEq)]
pub struct EnumType {
pub(crate) name: Cow<'static, str>,
// Associating a SpectaID will allow exporter to lookup more detailed information about the type to provide better errors.
pub(crate) sid: Option<SpectaID>,
// This is used to allow `serde_json::Number` and `toml::Value` to contain BigInt numbers without an error.
// I don't know if we should block bigints in these any types. Really I think we should but we need a good DX around overriding it on a per-type basis.
pub(crate) skip_bigint_checks: bool,
pub(crate) repr: EnumRepr,
pub(crate) generics: Vec<GenericType>,
Expand Down
1 change: 1 addition & 0 deletions src/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ impl<T: Into<DataType> + 'static> From<Vec<T>> for DataType {
fn from(t: Vec<T>) -> Self {
DataType::Enum(EnumType {
name: "Vec".into(),
sid: None,
repr: EnumRepr::Untagged,
skip_bigint_checks: false,
variants: t
Expand Down
4 changes: 3 additions & 1 deletion src/datatype/struct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;

use crate::{DataType, GenericType, NamedDataType, NamedFields, UnnamedFields};
use crate::{DataType, GenericType, NamedDataType, NamedFields, SpectaID, UnnamedFields};

#[derive(Debug, Clone, PartialEq)]
pub enum StructFields {
Expand All @@ -21,6 +21,8 @@ pub enum StructFields {
#[derive(Debug, Clone, PartialEq)]
pub struct StructType {
pub(crate) name: Cow<'static, str>,
// Associating a SpectaID will allow exporter to lookup more detailed information about the type to provide better errors.
pub(crate) sid: Option<SpectaID>,
pub(crate) generics: Vec<GenericType>,
pub(crate) fields: StructFields,
}
Expand Down
4 changes: 4 additions & 0 deletions src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ pub mod construct {

pub const fn r#struct(
name: Cow<'static, str>,
sid: Option<SpectaID>,
generics: Vec<GenericType>,
fields: StructFields,
) -> StructType {
StructType {
name,
sid,
generics,
fields,
}
Expand All @@ -67,13 +69,15 @@ pub mod construct {

pub const fn r#enum(
name: Cow<'static, str>,
sid: SpectaID,
repr: EnumRepr,
skip_bigint_checks: bool,
generics: Vec<GenericType>,
variants: Vec<(Cow<'static, str>, EnumVariant)>,
) -> EnumType {
EnumType {
name,
sid: Some(sid),
repr,
skip_bigint_checks,
generics,
Expand Down
5 changes: 5 additions & 0 deletions src/lang/ts/context.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::{borrow::Cow, fmt};

use crate::ImplLocation;

use super::ExportConfig;

#[derive(Clone, Debug)]
pub(crate) enum PathItem {
Type(Cow<'static, str>),
TypeExtended(Cow<'static, str>, ImplLocation),
Field(Cow<'static, str>),
Variant(Cow<'static, str>),
}
Expand Down Expand Up @@ -41,13 +44,15 @@ impl ExportPath {
while let Some(item) = path.next() {
s.push_str(match item {
PathItem::Type(v) => v,
PathItem::TypeExtended(_, loc) => loc.as_str(),
PathItem::Field(v) => v,
PathItem::Variant(v) => v,
});

if let Some(next) = path.peek() {
s.push_str(match next {
PathItem::Type(_) => " -> ",
PathItem::TypeExtended(_, _) => " -> ",
PathItem::Field(_) => ".",
PathItem::Variant(_) => "::",
});
Expand Down
17 changes: 14 additions & 3 deletions src/lang/ts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,19 @@ fn export_datatype_inner(
ctx: ExportContext,
typ @ NamedDataType {
name,
ext,
docs,
deprecated,
inner: item,
..
}: &NamedDataType,
type_map: &TypeMap,
) -> Output {
let ctx = ctx.with(PathItem::Type(name.clone()));
let ctx = ctx.with(
ext.clone()
.map(|v| PathItem::TypeExtended(name.clone(), v.impl_location))
.unwrap_or_else(|| PathItem::Type(name.clone())),
);
let name = sanitise_type_name(ctx.clone(), NamedLocation::Type, name)?;

let generics = item
Expand Down Expand Up @@ -187,7 +192,7 @@ pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &Type
BigIntExportBehavior::Number => NUMBER.into(),
BigIntExportBehavior::BigInt => BIGINT.into(),
BigIntExportBehavior::Fail => {
return Err(ExportError::BigIntForbidden(ctx.export_path()))
return Err(ExportError::BigIntForbidden(ctx.export_path()));
}
BigIntExportBehavior::FailWithReason(reason) => {
return Err(ExportError::Other(ctx.export_path(), reason.to_owned()))
Expand Down Expand Up @@ -241,7 +246,13 @@ pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &Type
}
}
DataType::Struct(item) => struct_datatype(
ctx.with(PathItem::Type(item.name().clone())),
ctx.with(
item.sid
.and_then(|sid| type_map.get(sid))
.and_then(|v| v.ext())
.map(|v| PathItem::TypeExtended(item.name().clone(), v.impl_location))
.unwrap_or_else(|| PathItem::Type(item.name().clone())),
),
item.name(),
item,
type_map,
Expand Down
6 changes: 5 additions & 1 deletion src/type/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ impl<T: Type> Type for std::ops::Range<T> {
let ty = Some(T::definition(opts));
DataType::Struct(StructType {
name: "Range".into(),
sid: None,
generics: vec![],
fields: StructFields::Named(NamedFields {
fields: vec![
Expand Down Expand Up @@ -299,6 +300,7 @@ const _: () = {
fn inline(_: DefOpts, _: &[DataType]) -> DataType {
DataType::Enum(EnumType {
name: "Number".into(),
sid: None,
repr: EnumRepr::Untagged,
skip_bigint_checks: true,
variants: vec![
Expand Down Expand Up @@ -396,6 +398,7 @@ const _: () = {
fn inline(_: DefOpts, _: &[DataType]) -> DataType {
DataType::Enum(EnumType {
name: "Number".into(),
sid: None,
repr: EnumRepr::Untagged,
skip_bigint_checks: true,
variants: vec![
Expand Down Expand Up @@ -669,6 +672,7 @@ impl<L: Type, R: Type> Type for either::Either<L, R> {
fn inline(opts: DefOpts, generics: &[DataType]) -> DataType {
DataType::Enum(EnumType {
name: "Either".into(),
sid: None,
repr: EnumRepr::Untagged,
skip_bigint_checks: false,
variants: vec![
Expand Down Expand Up @@ -727,7 +731,7 @@ impl<L: Type, R: Type> Type for either::Either<L, R> {
#[cfg(feature = "bevy_ecs")]
const _: () = {
#[derive(Type)]
#[specta(rename = "bevy_ecs::entity::Entity", remote = bevy_ecs::entity::Entity, crate = crate, export = false)]
#[specta(rename = "Entity", remote = bevy_ecs::entity::Entity, crate = crate, export = false)]
#[allow(dead_code)]
struct EntityDef(u64);
};
11 changes: 9 additions & 2 deletions src/type/map.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
use std::collections::BTreeMap;
use std::{collections::BTreeMap, fmt};

use crate::{NamedDataType, SpectaID};

/// A map used to store the types "discovered" while exporting a type.
#[derive(Debug, Default, Clone, PartialEq)]
#[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<SpectaID, Option<NamedDataType>>,
// A stack of types that are currently being flattened. This is used to detect cycles.
pub(crate) flatten_stack: Vec<SpectaID>,
}

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> {
Expand Down
19 changes: 17 additions & 2 deletions tests/remote_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,24 @@ fn typescript_types_glam() {
#[test]
#[cfg(feature = "bevy_ecs")]
fn typescript_types_bevy_ecs() {
use specta::ts::ExportPath;
use specta::ts::{self, BigIntExportBehavior, ExportConfig, ExportPath};

use crate::ts::assert_ts;

assert_ts!(error; bevy_ecs::entity::Entity, specta::ts::ExportError::BigIntForbidden(ExportPath::new_unsafe("bevy_ecs::entity::Entity -> u64")));
assert_eq!(
ts::inline::<bevy_ecs::entity::Entity>(
&ExportConfig::default().bigint(BigIntExportBehavior::Number)
),
Ok("number".into())
);
// TODO: As we inline `Entity` never ends up in the type map so it falls back to "Entity" in the error instead of the path to the type. Is this what we want or not?
assert_ts!(error; bevy_ecs::entity::Entity, specta::ts::ExportError::BigIntForbidden(ExportPath::new_unsafe("Entity -> u64")));

// https://github.com/oscartbeaumont/specta/issues/161#issuecomment-1822735951
assert_eq!(
ts::export::<bevy_ecs::entity::Entity>(
&ExportConfig::default().bigint(BigIntExportBehavior::Number)
),
Ok("export Entity = number;".into())
);
}
6 changes: 3 additions & 3 deletions tests/reserved_keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,23 @@ fn test_ts_reserved_keyworks() {
specta::ts::export::<astruct::r#enum>(&ExportConfig::default()),
Err(ExportError::ForbiddenName(
NamedLocation::Type,
ExportPath::new_unsafe("enum"),
ExportPath::new_unsafe("tests/reserved_keywords.rs:10:14"),
"enum"
))
);
assert_eq!(
specta::ts::export::<atuplestruct::r#enum>(&ExportConfig::default()),
Err(ExportError::ForbiddenName(
NamedLocation::Type,
ExportPath::new_unsafe("enum"),
ExportPath::new_unsafe("tests/reserved_keywords.rs:22:14"),
"enum"
))
);
assert_eq!(
specta::ts::export::<aenum::r#enum>(&ExportConfig::default()),
Err(ExportError::ForbiddenName(
NamedLocation::Type,
ExportPath::new_unsafe("enum"),
ExportPath::new_unsafe("tests/reserved_keywords.rs:32:14"),
"enum"
))
);
Expand Down
4 changes: 2 additions & 2 deletions tests/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,12 @@ fn typescript_types() {
assert_ts_export!(
error;
RenameWithWeirdCharsStruct,
ExportError::InvalidName(NamedLocation::Type, ExportPath::new_unsafe("@odata.context"), r#"@odata.context"#.to_string())
ExportError::InvalidName(NamedLocation::Type, ExportPath::new_unsafe("tests/ts.rs:599:10"), r#"@odata.context"#.to_string())
);
assert_ts_export!(
error;
RenameWithWeirdCharsEnum,
ExportError::InvalidName(NamedLocation::Type, ExportPath::new_unsafe("@odata.context"), r#"@odata.context"#.to_string())
ExportError::InvalidName(NamedLocation::Type, ExportPath::new_unsafe("tests/ts.rs:603:10"), r#"@odata.context"#.to_string())
);

// https://github.com/oscartbeaumont/specta/issues/156
Expand Down

0 comments on commit 72c8ee7

Please sign in to comment.