From 734ead1115efab3fe5cac004d11fc8d948645218 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Sun, 24 Dec 2023 21:29:20 +0800 Subject: [PATCH 1/4] wip --- src/datatype/literal.rs | 1 + src/datatype/mod.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/datatype/literal.rs b/src/datatype/literal.rs index 4d45ab4f..d6ea68c6 100644 --- a/src/datatype/literal.rs +++ b/src/datatype/literal.rs @@ -22,6 +22,7 @@ pub enum LiteralType { String(String), char(char), /// Standalone `null` without a known type + // TODO: Remove this None, } diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index 310b88c3..1d25e809 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -29,24 +29,24 @@ use crate::SpectaID; /// A language exporter takes this general format and converts it into a language specific syntax. #[derive(Debug, Clone, PartialEq)] pub enum DataType { - // Always inlined - Any, - Unknown, + Nullable(Box), Primitive(PrimitiveType), Literal(LiteralType), - /// Either a `Set` or a `Vec` List(List), - Nullable(Box), Map(Box<(DataType, DataType)>), - // Anonymous Reference types - Struct(StructType), Enum(EnumType), Tuple(TupleType), - // Result - Result(Box<(DataType, DataType)>), - // A reference type that has already been defined Reference(DataTypeReference), Generic(GenericType), + + // TODO: Should we keep this - https://github.com/oscartbeaumont/specta/issues/192 + Result(Box<(DataType, DataType)>), + + // TODO: Remove these + Any, + Unknown, + Struct(StructType), + // Named(NamedDataType) } impl DataType { From 9710d1af228a1fde689d6c2a165878d31c1c7f3f Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Sun, 24 Dec 2023 21:50:41 +0800 Subject: [PATCH 2/4] Break `Map` out to a dedicated type --- src/datatype/list.rs | 6 ++++ src/datatype/map.rs | 37 +++++++++++++++++++++++ src/datatype/mod.rs | 4 ++- src/lang/ts/mod.rs | 4 +-- src/serde.rs | 16 +++++----- src/type/impls.rs | 11 ++++--- src/type/macros.rs | 50 ++++++++++++++++++++------------ tests/macro/compile_error.stderr | 20 +++++++++++-- 8 files changed, 113 insertions(+), 35 deletions(-) create mode 100644 src/datatype/map.rs diff --git a/src/datatype/list.rs b/src/datatype/list.rs index eca4cf5e..7a83cbda 100644 --- a/src/datatype/list.rs +++ b/src/datatype/list.rs @@ -6,6 +6,8 @@ pub struct List { pub(crate) ty: Box, // Length is set for `[Type; N]` arrays. pub(crate) length: Option, + // Are each elements unique? Eg. `HashSet` or `BTreeSet` + pub(crate) unique: bool, } impl List { @@ -16,4 +18,8 @@ impl List { pub fn length(&self) -> Option { self.length } + + pub fn unique(&self) -> bool { + self.unique + } } diff --git a/src/datatype/map.rs b/src/datatype/map.rs new file mode 100644 index 00000000..6a5d5828 --- /dev/null +++ b/src/datatype/map.rs @@ -0,0 +1,37 @@ +use crate::DataType; + +#[derive(Debug, Clone, PartialEq)] + +pub struct Map { + // TODO: Box these fields together as an internal optimization. + // The type of the map keys. + pub(crate) key_ty: Box, + // The type of the map values. + pub(crate) value_ty: Box, + // Are each elements unique? Eg. `HashSet` or `BTreeSet` + pub(crate) unique: bool, +} + +impl Map { + // TODO: `inline` vs `reference` is a thing people downstream need to think about. + // TODO: Should this need `generics` + // pub fn new(type_map: &mut TypeMap, generics: &[DataType]) -> Self { + // Self { + // key_ty: Box::new(K::inline(type_map, generics)), + // value_ty: Box::new(V::inline(type_map, generics)), + // unique: false, + // } + // } + + pub fn key_ty(&self) -> &DataType { + &self.key_ty + } + + pub fn value_ty(&self) -> &DataType { + &self.value_ty + } + + pub fn unique(&self) -> bool { + self.unique + } +} diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index 1d25e809..0b99be51 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -7,6 +7,7 @@ mod r#enum; mod fields; mod list; mod literal; +mod map; mod named; mod primitive; pub mod reference; @@ -16,6 +17,7 @@ mod tuple; pub use fields::*; pub use list::*; pub use literal::*; +pub use map::*; pub use named::*; pub use primitive::*; pub use r#enum::*; @@ -33,7 +35,7 @@ pub enum DataType { Primitive(PrimitiveType), Literal(LiteralType), List(List), - Map(Box<(DataType, DataType)>), + Map(Map), Enum(EnumType), Tuple(TupleType), Reference(DataTypeReference), diff --git a/src/lang/ts/mod.rs b/src/lang/ts/mod.rs index 022088cd..0d2f775a 100644 --- a/src/lang/ts/mod.rs +++ b/src/lang/ts/mod.rs @@ -255,8 +255,8 @@ pub(crate) fn datatype_inner(ctx: ExportContext, typ: &DataType, type_map: &Type format!( // We use this isn't of `Record` to avoid issues with circular references. "{{ [key in {}]: {} }}", - datatype_inner(ctx.clone(), &def.0, type_map)?, - datatype_inner(ctx, &def.1, type_map)? + datatype_inner(ctx.clone(), def.key_ty(), type_map)?, + datatype_inner(ctx, def.value_ty(), type_map)? ) } // We use `T[]` instead of `Array` to avoid issues with circular references. diff --git a/src/serde.rs b/src/serde.rs index c1ba3ff9..1fa276bc 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -4,7 +4,7 @@ use thiserror::Error; use crate::{ internal::{skip_fields, skip_fields_named}, - DataType, EnumRepr, EnumType, EnumVariants, GenericType, List, LiteralType, PrimitiveType, + DataType, EnumRepr, EnumType, EnumVariants, GenericType, List, LiteralType, Map, PrimitiveType, SpectaID, StructFields, TypeMap, }; @@ -35,8 +35,8 @@ fn is_valid_ty_internal( match dt { DataType::Nullable(ty) => is_valid_ty(ty, type_map)?, DataType::Map(ty) => { - is_valid_map_key(&ty.0, type_map)?; - is_valid_ty_internal(&ty.1, type_map, checked_references)?; + is_valid_map_key(ty.key_ty(), type_map)?; + is_valid_ty_internal(ty.value_ty(), type_map, checked_references)?; } DataType::Struct(ty) => match ty.fields() { StructFields::Unit => {} @@ -251,12 +251,14 @@ fn resolve_generics(mut dt: DataType, generics: &Vec<(GenericType, DataType)>) - DataType::List(v) => DataType::List(List { ty: Box::new(resolve_generics(*v.ty, generics)), length: v.length, + unique: v.unique, }), DataType::Nullable(v) => DataType::Nullable(Box::new(resolve_generics(*v, generics))), - DataType::Map(v) => DataType::Map(Box::new({ - let (k, v) = *v; - (resolve_generics(k, generics), resolve_generics(v, generics)) - })), + DataType::Map(v) => DataType::Map(Map { + key_ty: Box::new(resolve_generics(*v.key_ty, generics)), + value_ty: Box::new(resolve_generics(*v.value_ty, generics)), + unique: v.unique, + }), DataType::Struct(ref mut v) => match &mut v.fields { StructFields::Unit => dt, StructFields::Unnamed(f) => { diff --git a/src/type/impls.rs b/src/type/impls.rs index aba600d7..1b3260f1 100644 --- a/src/type/impls.rs +++ b/src/type/impls.rs @@ -121,6 +121,7 @@ impl Type for [T; N] { }, ), length: Some(N), + unique: false, // TODO: Properly hook this up }) } @@ -135,6 +136,7 @@ impl Type for [T; N] { }, ), length: Some(N), + unique: false, // TODO: Properly hook this up }), } } @@ -369,10 +371,11 @@ const _: () = { impl Type for serde_yaml::value::TaggedValue { fn inline(_: &mut TypeMap, _: &[DataType]) -> DataType { - DataType::Map(Box::new(( - DataType::Primitive(PrimitiveType::String), - DataType::Unknown, - ))) + DataType::Map(crate::datatype::Map { + key_ty: Box::new(DataType::Primitive(PrimitiveType::String)), + value_ty: Box::new(DataType::Unknown), + unique: false, // TODO: Properly hook this up + }) } } diff --git a/src/type/macros.rs b/src/type/macros.rs index 3052a0e6..0937f7a2 100644 --- a/src/type/macros.rs +++ b/src/type/macros.rs @@ -108,6 +108,7 @@ macro_rules! impl_for_list { generics, ))), length: None, + unique: false, // TODO: Properly hook this up }) } @@ -118,6 +119,7 @@ macro_rules! impl_for_list { || T::reference(type_map, generics).inner, )), length: None, + unique: false, // TODO: Properly hook this up }), } } @@ -129,30 +131,42 @@ macro_rules! impl_for_map { ($ty:path as $name:expr) => { impl Type for $ty { fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> DataType { - DataType::Map(Box::new(( - generics - .get(0) - .cloned() - .unwrap_or_else(|| K::inline(type_map, generics)), - generics - .get(1) - .cloned() - .unwrap_or_else(|| V::inline(type_map, generics)), - ))) - } - - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { - Reference { - inner: DataType::Map(Box::new(( + // TODO: This is a private-only API. We need a public alternative that somehow deals with `inline` vs `reference`. + DataType::Map(crate::datatype::Map { + key_ty: Box::new( generics .get(0) .cloned() - .unwrap_or_else(|| K::reference(type_map, generics).inner), + .unwrap_or_else(|| K::inline(type_map, generics)), + ), + value_ty: Box::new( generics .get(1) .cloned() - .unwrap_or_else(|| V::reference(type_map, generics).inner), - ))), + .unwrap_or_else(|| V::inline(type_map, generics)), + ), + unique: false, // TODO: Properly hook this up + }) + } + + fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { + Reference { + // TODO: This is a private-only API. We need a public alternative that somehow deals with `inline` vs `reference`. + inner: DataType::Map(crate::datatype::Map { + key_ty: Box::new( + generics + .get(0) + .cloned() + .unwrap_or_else(|| K::reference(type_map, generics).inner), + ), + value_ty: Box::new( + generics + .get(1) + .cloned() + .unwrap_or_else(|| V::reference(type_map, generics).inner), + ), + unique: false, // TODO: Properly hook this up + }), } } } diff --git a/tests/macro/compile_error.stderr b/tests/macro/compile_error.stderr index c84438a3..428773ba 100644 --- a/tests/macro/compile_error.stderr +++ b/tests/macro/compile_error.stderr @@ -52,11 +52,25 @@ error: specta: invalid formatted attribute 103 | #[specta = "todo"] | ^^^^^^ +error: cannot find attribute `specta` in this scope + --> tests/macro/compile_error.rs:107:3 + | +107 | #[specta] + | ^^^^^^ + | + = note: `specta` is in scope, but it is a crate, not an attribute +help: consider importing one of these items + | +3 + use specta::specta; + | +3 + use specta_macros::specta; + | + error[E0601]: `main` function not found in crate `$CRATE` - --> tests/macro/compile_error.rs:104:36 + --> tests/macro/compile_error.rs:108:20 | -104 | pub struct InvalidSpectaAttribute2; - | ^ consider adding a `main` function to `$DIR/tests/macro/compile_error.rs` +108 | pub fn testing() {} + | ^ consider adding a `main` function to `$DIR/tests/macro/compile_error.rs` error[E0277]: the trait bound `UnitExternal: specta::Flatten` is not satisfied --> tests/macro/compile_error.rs:32:11 From 1c70ec44b6baf49052a98dc51d5d1e36475f4a36 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Mon, 25 Dec 2023 00:36:30 +0800 Subject: [PATCH 3/4] wip: more remote impls --- src/datatype/mod.rs | 7 +- src/datatype/named.rs | 9 +- src/type/impls.rs | 285 +++++------------------------------------- 3 files changed, 42 insertions(+), 259 deletions(-) diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index 0b99be51..cfcd90c2 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -31,7 +31,6 @@ use crate::SpectaID; /// A language exporter takes this general format and converts it into a language specific syntax. #[derive(Debug, Clone, PartialEq)] pub enum DataType { - Nullable(Box), Primitive(PrimitiveType), Literal(LiteralType), List(List), @@ -41,6 +40,9 @@ pub enum DataType { Reference(DataTypeReference), Generic(GenericType), + // TODO: Can we avoid these being nestable + Nullable(Box), + // TODO: Should we keep this - https://github.com/oscartbeaumont/specta/issues/192 Result(Box<(DataType, DataType)>), @@ -48,13 +50,14 @@ pub enum DataType { Any, Unknown, Struct(StructType), + // TODO: Introduce this // Named(NamedDataType) } impl DataType { pub fn generics(&self) -> Option<&Vec> { match self { - Self::Struct(s) => Some(s.generics()), + // Self::Struct(s) => Some(s.generics()), Self::Enum(e) => Some(e.generics()), _ => None, } diff --git a/src/datatype/named.rs b/src/datatype/named.rs index 60d3a9fe..c4527ab0 100644 --- a/src/datatype/named.rs +++ b/src/datatype/named.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::{DataType, DeprecatedType, ImplLocation, SpectaID}; +use crate::{DataType, DeprecatedType, EnumType, ImplLocation, SpectaID, StructType}; /// A NamedDataTypeImpl includes extra information which is only available for [NamedDataType]'s that come from a real Rust type. #[derive(Debug, Clone, PartialEq)] @@ -38,6 +38,13 @@ pub struct NamedDataType { pub inner: DataType, } +// // TODO: Rename this +// #[derive(Debug, Clone, PartialEq)] +// pub enum NamedDataTypeTODO { +// Struct(StructType), +// Enum(EnumType), // TODO +// } + impl NamedDataType { pub fn name(&self) -> &Cow<'static, str> { &self.name diff --git a/src/type/impls.rs b/src/type/impls.rs index 1b3260f1..abcb1732 100644 --- a/src/type/impls.rs +++ b/src/type/impls.rs @@ -192,42 +192,16 @@ impl Type for std::marker::PhantomData { #[allow(unused)] #[derive(Type)] #[specta(remote = std::convert::Infallible, crate = crate, export = false)] -pub enum Infallible {} - -impl Type for std::ops::Range { - fn inline(type_map: &mut TypeMap, _generics: &[DataType]) -> DataType { - let ty = Some(T::definition(type_map)); - DataType::Struct(StructType { - name: "Range".into(), - sid: None, - generics: vec![], - fields: StructFields::Named(NamedFields { - fields: vec![ - ( - "start".into(), - Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: ty.clone(), - }, - ), - ( - "end".into(), - Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty, - }, - ), - ], - tag: None, - }), - }) - } +enum Infallible {} + +#[allow(unused)] +#[derive(Type)] +#[specta(remote = std::ops::Range, crate = crate, export = false)] +struct Range { + // TODO: Why is this introducing `'static` bound??? + // TODO: Flatten these fields??? + start: T, + end: T, } impl Type for std::ops::RangeInclusive { @@ -280,69 +254,13 @@ const _: () = { Object(Map), } - impl Type for Number { - fn inline(_: &mut TypeMap, _: &[DataType]) -> DataType { - DataType::Enum(EnumType { - name: "Number".into(), - sid: None, - repr: EnumRepr::Untagged, - skip_bigint_checks: true, - variants: vec![ - ( - "f64".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(DataType::Primitive(PrimitiveType::f64)), - }], - }), - }, - ), - ( - "i64".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(DataType::Primitive(PrimitiveType::i64)), - }], - }), - }, - ), - ( - "u64".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(DataType::Primitive(PrimitiveType::u64)), - }], - }), - }, - ), - ], - generics: vec![], - }) - } + #[allow(non_camel_case_types)] + #[derive(Type)] + #[specta(remote = Number, crate = crate, export = false)] + enum NumberDef { + f64(f64), + i64(i64), + u64(u64), } }; @@ -379,69 +297,13 @@ const _: () = { } } - impl Type for serde_yaml::Number { - fn inline(_: &mut TypeMap, _: &[DataType]) -> DataType { - DataType::Enum(EnumType { - name: "Number".into(), - sid: None, - repr: EnumRepr::Untagged, - skip_bigint_checks: true, - variants: vec![ - ( - "f64".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(DataType::Primitive(PrimitiveType::f64)), - }], - }), - }, - ), - ( - "i64".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(DataType::Primitive(PrimitiveType::i64)), - }], - }), - }, - ), - ( - "u64".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(DataType::Primitive(PrimitiveType::u64)), - }], - }), - }, - ), - ], - generics: vec![], - }) - } + #[allow(non_camel_case_types)] + #[derive(Type)] + #[specta(remote = serde_yaml::Number, crate = crate, export = false)] + enum NumberDef { + f64(f64), + i64(i64), + u64(u64), } }; @@ -649,100 +511,11 @@ const _: () = { impl_as!(url::Url as String); #[cfg(feature = "either")] -impl Type for either::Either { - fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> DataType { - DataType::Enum(EnumType { - name: "Either".into(), - sid: None, - repr: EnumRepr::Untagged, - skip_bigint_checks: false, - variants: vec![ - ( - "Left".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(L::inline(type_map, generics)), - }], - }), - }, - ), - ( - "Right".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(R::inline(type_map, generics)), - }], - }), - }, - ), - ], - generics: vec![], - }) - } - - fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { - Reference { - inner: DataType::Enum(EnumType { - name: "Either".into(), - sid: None, - repr: EnumRepr::Untagged, - skip_bigint_checks: false, - variants: vec![ - ( - "Left".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(L::reference(type_map, generics).inner), - }], - }), - }, - ), - ( - "Right".into(), - EnumVariant { - skip: false, - docs: Cow::Borrowed(""), - deprecated: None, - inner: EnumVariants::Unnamed(UnnamedFields { - fields: vec![Field { - optional: false, - flatten: false, - deprecated: None, - docs: Cow::Borrowed(""), - ty: Some(R::reference(type_map, generics).inner), - }], - }), - }, - ), - ], - generics: vec![], - }), - } - } +#[derive(Type)] +#[specta(untagged, remote = either::Either, crate = crate, export = false)] +enum Either { + Left(L), + Right(R), } #[cfg(feature = "bevy_ecs")] From d5f311f9ffa4d4be1920cb4656317171cca9de8d Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Mon, 25 Dec 2023 13:29:21 +0800 Subject: [PATCH 4/4] wip --- examples/demo.rs | 13 +++++++++++++ src/datatype/mod.rs | 2 ++ src/datatype/reference.rs | 2 +- src/datatype/type.rs | 41 +++++++++++++++++++++++++++++++++++++++ src/internal.rs | 31 +++++++++++++++++++++++++++++ src/lang/ts/mod.rs | 2 +- src/lib.rs | 2 +- src/static_types.rs | 10 +++++----- src/type/macros.rs | 6 ++++-- src/type/mod.rs | 10 +++++++--- src/type/specta_id.rs | 5 +++++ 11 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 examples/demo.rs create mode 100644 src/datatype/type.rs diff --git a/examples/demo.rs b/examples/demo.rs new file mode 100644 index 00000000..65ec914a --- /dev/null +++ b/examples/demo.rs @@ -0,0 +1,13 @@ +use specta::{datatype::TypeImpl, Type}; + +#[derive(Type)] +pub struct Demo { + a: String, +} + +fn main() { + // let a = TypeImpl::new::(); + // let b = TypeImpl::new::(); + // let c = TypeImpl::new::(); + // println!("{a:?}\n{b:?}\n{c:?}"); +} diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index cfcd90c2..e7e1a4b4 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -13,6 +13,7 @@ mod primitive; pub mod reference; mod r#struct; mod tuple; +mod r#type; pub use fields::*; pub use list::*; @@ -22,6 +23,7 @@ pub use named::*; pub use primitive::*; pub use r#enum::*; pub use r#struct::*; +pub use r#type::*; pub use tuple::*; use crate::SpectaID; diff --git a/src/datatype/reference.rs b/src/datatype/reference.rs index b3ab2b06..662351d5 100644 --- a/src/datatype/reference.rs +++ b/src/datatype/reference.rs @@ -12,7 +12,7 @@ pub struct Reference { pub fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { Reference { - inner: T::inline(type_map, generics), + inner: T::inline(type_map, generics).ty, } } diff --git a/src/datatype/type.rs b/src/datatype/type.rs new file mode 100644 index 00000000..dd0825e2 --- /dev/null +++ b/src/datatype/type.rs @@ -0,0 +1,41 @@ +use std::{any::TypeId, panic::Location}; + +use crate::{internal::type_id::non_static_type_id, DataType, Type}; + +#[derive(Debug)] +pub struct TypeImpl { + name: &'static str, + tid: TypeId, + file: &'static str, + line: u32, + column: u32, + // TODO: Not `pub` + pub ty: DataType, +} + +// TODO: Debug impl + ordering + +impl TypeImpl { + // TODO: Do we make this private for the macro only??? Probs + // TODO: Anyone could call this on any type but the `caller` information will be inconsistent. + #[track_caller] + pub fn new(ty: DataType) -> Self { + let caller = Location::caller(); + Self { + name: std::any::type_name::(), + tid: non_static_type_id::(), + file: caller.file(), + line: caller.line(), + column: caller.column(), + ty, + } + } + + // TODO: Field accessors + + // TODO: Iterator for module path derived from `name` field + + pub fn is() -> bool { + todo!(); + } +} diff --git a/src/internal.rs b/src/internal.rs index c9d7b6b1..06b6cfc3 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -249,3 +249,34 @@ mod functions { } #[cfg(feature = "functions")] pub use functions::*; + +// This code is taken from `erased-serde` - https://github.com/dtolnay/erased-serde/blob/master/src/any.rs +#[allow(unsafe_code)] +pub(crate) mod type_id { + use std::{any::TypeId, marker::PhantomData}; + + trait NonStaticAny { + fn get_type_id(&self) -> TypeId + where + Self: 'static; + } + + impl NonStaticAny for PhantomData { + fn get_type_id(&self) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + } + + pub fn non_static_type_id() -> TypeId { + let non_static_thing = PhantomData::; + let thing = unsafe { + std::mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>( + &non_static_thing, + ) + }; + NonStaticAny::get_type_id(thing) + } +} diff --git a/src/lang/ts/mod.rs b/src/lang/ts/mod.rs index 0d2f775a..e56d2743 100644 --- a/src/lang/ts/mod.rs +++ b/src/lang/ts/mod.rs @@ -59,7 +59,7 @@ pub fn inline_ref(_: &T, conf: &ExportConfig) -> Output { /// Eg. `{ demo: string; };` pub fn inline(conf: &ExportConfig) -> Output { let mut type_map = TypeMap::default(); - let ty = T::inline(&mut type_map, &[]); + let ty = T::inline(&mut type_map, &[]).ty; is_valid_ty(&ty, &type_map)?; let result = datatype(conf, &ty, &type_map); diff --git a/src/lib.rs b/src/lib.rs index 7beef1db..3625f41d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![doc = include_str!("./docs.md")] -#![forbid(unsafe_code)] +#![deny(unsafe_code)] #![warn(clippy::all, clippy::unwrap_used, clippy::panic)] // TODO: missing_docs #![allow(clippy::module_inception)] #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/src/static_types.rs b/src/static_types.rs index f65ed005..ee745655 100644 --- a/src/static_types.rs +++ b/src/static_types.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use crate::{DataType, Type, TypeMap}; +use crate::{DataType, Type, TypeImpl, TypeMap}; /// Easily convert a non-Specta type into a Specta compatible type. /// This will be typed as `any` in Typescript. @@ -34,8 +34,8 @@ use crate::{DataType, Type, TypeMap}; pub struct Any(T); impl Type for Any { - fn inline(_: &mut TypeMap, _: &[DataType]) -> DataType { - DataType::Any + fn inline(_: &mut TypeMap, _: &[DataType]) -> TypeImpl { + TypeImpl::new::(DataType::Any) } } @@ -97,8 +97,8 @@ impl serde::Serialize for Any { pub struct Unknown(T); impl Type for Unknown { - fn inline(_: &mut TypeMap, _: &[DataType]) -> DataType { - DataType::Unknown + fn inline(_: &mut TypeMap, _: &[DataType]) -> TypeImpl { + TypeImpl::new::(DataType::Unknown) } } diff --git a/src/type/macros.rs b/src/type/macros.rs index 0937f7a2..eaa1dee2 100644 --- a/src/type/macros.rs +++ b/src/type/macros.rs @@ -1,11 +1,13 @@ macro_rules! impl_passthrough { ($t:ty) => { fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> DataType { - <$t>::inline(type_map, generics) + // <$t>::inline(type_map, generics) + todo!(); } fn reference(type_map: &mut TypeMap, generics: &[DataType]) -> Reference { - <$t>::reference(type_map, generics) + // <$t>::reference(type_map, generics) + todo!(); } }; } diff --git a/src/type/mod.rs b/src/type/mod.rs index e465494a..0f6adf5c 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -9,7 +9,7 @@ pub use map::*; pub use post_process::*; pub use specta_id::*; -use crate::{reference, DataType, NamedDataType}; +use crate::{reference, DataType, NamedDataType, TypeImpl}; use self::reference::Reference; @@ -21,16 +21,19 @@ pub trait Type { /// [`definition`](crate::Type::definition) and [`reference`](crate::Type::definition) /// /// Implemented internally or via the [`Type`](derive@crate::Type) macro - fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> DataType; + fn inline(type_map: &mut TypeMap, generics: &[DataType]) -> TypeImpl; + // TODO: Remove this in favor of it being an implementation detail of `inline`/`reference`??? /// Small wrapper around [`inline`](crate::Type::inline) that provides /// [`definition_generics`](crate::Type::definition_generics) /// as the value for the `generics` arg. /// /// If your type is generic you *must* override the default implementation! + // TODO: TypeImpl fn definition(type_map: &mut TypeMap) -> DataType { // TODO: Remove this default impl? - Self::inline(type_map, &[]) + // Self::inline(type_map, &[]) + todo!(); } /// Generates a datatype corresponding to a reference to this type, @@ -42,6 +45,7 @@ pub trait Type { } } +// TODO: This probs needs to go now because it's not relevant. /// NamedType represents a type that can be converted into [NamedDataType]. /// This will be implemented for all types with the [Type] derive macro. pub trait NamedType: Type { diff --git a/src/type/specta_id.rs b/src/type/specta_id.rs index 3d161583..bee74b8b 100644 --- a/src/type/specta_id.rs +++ b/src/type/specta_id.rs @@ -49,6 +49,11 @@ impl PartialEq for SpectaID { pub struct ImplLocation(pub(crate) &'static str); impl ImplLocation { + #[track_caller] + pub fn new() -> Self { + Self(concat!(file!(), ":", line!(), ":", column!())) + } + /// Get the location as a string pub const fn as_str(&self) -> &'static str { self.0