Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert fixed size Rust array to fixed size TS tuple #209

Merged
merged 8 commits into from
Jan 30, 2024
12 changes: 9 additions & 3 deletions macros/src/types/generics.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
GenericArgument, GenericParam, Generics, ItemStruct, PathArguments, Type, TypeArray, TypeGroup,
GenericArgument, GenericParam, Generics, ItemStruct, PathArguments, Type, TypeGroup,
TypeReference, TypeSlice, TypeTuple,
};

Expand Down Expand Up @@ -79,9 +79,15 @@ pub fn format_type(ty: &Type, dependencies: &mut Dependencies, generics: &Generi

// special treatment for arrays and tuples
match ty {
// The field is an array (`[T; n]`) or a slice (`[T]`) so it technically doesn't have a
// Arrays have their own implementation that needs to be handle separetly
// be cause the T in `[T; N]` is technically not a generic
Type::Array(type_array) => {
let formatted = format_type(&type_array.elem, dependencies, generics);
return quote!(<#type_array>::name_with_type_args(vec![#formatted]))
}
// The field is a slice (`[T]`) so it technically doesn't have a
// generic argument. Therefore, we handle it explicitly here like a `Vec<T>`
Type::Array(TypeArray { ref elem, .. }) | Type::Slice(TypeSlice { ref elem, .. }) => {
Type::Slice(TypeSlice { ref elem, .. }) => {
let inner_ty = elem;
let vec_ty = syn::parse2::<Type>(quote!(Vec::<#inner_ty>)).unwrap();
return format_type(&vec_ty, dependencies, generics);
Expand Down
49 changes: 45 additions & 4 deletions ts-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,18 +498,60 @@ impl<T: TS> TS for Vec<T> {
"Array".to_owned()
}

fn inline() -> String {
format!("Array<{}>", T::inline())
}

fn dependencies() -> Vec<Dependency>
where
Self: 'static,
{
[Dependency::from_ty::<T>()].into_iter().flatten().collect()
}

fn transparent() -> bool {
true
}
}

// Arrays longer than this limit will be emmited as Array<T>
const ARRAY_TUPLE_LIMIT: usize = 100; // Temp value
escritorio-gustavo marked this conversation as resolved.
Show resolved Hide resolved
impl<T: TS, const N: usize> TS for [T; N] {
fn name() -> String {
if N > ARRAY_TUPLE_LIMIT {
return Vec::<T>::name()
}

"[]".to_owned()
}

fn name_with_type_args(args: Vec<String>) -> String {
if N > ARRAY_TUPLE_LIMIT {
return Vec::<T>::name_with_type_args(args);
}

assert_eq!(
args.len(),
1,
"called Vec::name_with_type_args with {} args",
"called [T; N]::name_with_type_args with {} args",
args.len()
);
format!("Array<{}>", args[0])

format!(
"[{}]",
(0..N).map(|_| args[0].clone()).collect::<Box<[_]>>().join(", ")
)
}

fn inline() -> String {
format!("Array<{}>", T::inline())
if N > ARRAY_TUPLE_LIMIT {
return Vec::<T>::inline();
}

format!(
"[{}]",
(0..N).map(|_| T::inline()).collect::<Box<[_]>>().join(", ")
)
}

fn dependencies() -> Vec<Dependency>
Expand Down Expand Up @@ -616,7 +658,6 @@ impl_shadow!(as T: impl<'a, T: TS + ?Sized> TS for &T);
impl_shadow!(as Vec<T>: impl<T: TS, H> TS for HashSet<T, H>);
impl_shadow!(as Vec<T>: impl<T: TS> TS for BTreeSet<T>);
impl_shadow!(as HashMap<K, V>: impl<K: TS, V: TS> TS for BTreeMap<K, V>);
impl_shadow!(as Vec<T>: impl<T: TS, const N: usize> TS for [T; N]);
impl_shadow!(as Vec<T>: impl<T: TS> TS for [T]);

impl_wrapper!(impl<T: TS + ?Sized> TS for Box<T>);
Expand Down
10 changes: 5 additions & 5 deletions ts-rs/tests/arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ use ts_rs::TS;

#[test]
fn free() {
assert_eq!(<[String; 10]>::inline(), "Array<string>")
assert_eq!(<[String; 4]>::inline(), "[string, string, string, string]")
}

#[test]
fn interface() {
#[derive(TS)]
struct Interface {
#[allow(dead_code)]
a: [i32; 10],
a: [i32; 4],
}

assert_eq!(Interface::inline(), "{ a: Array<number>, }")
assert_eq!(Interface::inline(), "{ a: [number, number, number, number], }")
}

#[test]
fn newtype() {
#[derive(TS)]
struct Newtype(#[allow(dead_code)] [i32; 10]);
struct Newtype(#[allow(dead_code)] [i32; 4]);

assert_eq!(Newtype::inline(), "Array<number>")
assert_eq!(Newtype::inline(), "[number, number, number, number]")
}
8 changes: 4 additions & 4 deletions ts-rs/tests/generic_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn named() {
}
assert_eq!(
Struct::inline(),
"{ a: Array<string>, b: [Array<string>, Array<string>], c: Array<Array<string>>, }"
"{ a: Array<string>, b: [Array<string>, Array<string>], c: [Array<string>, Array<string>, Array<string>], }"
);
}

Expand All @@ -52,7 +52,7 @@ fn named_nested() {
b: (Vec<Vec<String>>, Vec<Vec<String>>),
c: [Vec<Vec<String>>; 3],
}
assert_eq!(Struct::inline(), "{ a: Array<Array<string>>, b: [Array<Array<string>>, Array<Array<string>>], c: Array<Array<Array<string>>>, }");
assert_eq!(Struct::inline(), "{ a: Array<Array<string>>, b: [Array<Array<string>>, Array<Array<string>>], c: [Array<Array<string>>, Array<Array<string>>, Array<Array<string>>], }");
}

#[test]
Expand All @@ -61,7 +61,7 @@ fn tuple() {
struct Tuple(Vec<i32>, (Vec<i32>, Vec<i32>), [Vec<i32>; 3]);
assert_eq!(
Tuple::inline(),
"[Array<number>, [Array<number>, Array<number>], Array<Array<number>>]"
"[Array<number>, [Array<number>, Array<number>], [Array<number>, Array<number>, Array<number>]]"
);
}

Expand All @@ -75,6 +75,6 @@ fn tuple_nested() {
);
assert_eq!(
Tuple::inline(),
"[Array<Array<number>>, [Array<Array<number>>, Array<Array<number>>], Array<Array<Array<number>>>]"
"[Array<Array<number>>, [Array<Array<number>>, Array<Array<number>>], [Array<Array<number>>, Array<Array<number>>, Array<Array<number>>]]"
);
}
7 changes: 4 additions & 3 deletions ts-rs/tests/generics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![allow(clippy::box_collection)]
#![allow(clippy::box_collection, clippy::enum_variant_names, dead_code)]
#![allow(dead_code)]

use std::{
Expand Down Expand Up @@ -142,7 +142,7 @@ fn generic_struct() {

assert_eq!(
Struct::<()>::decl(),
"type Struct<T> = { a: T, b: [T, T], c: [T, [T, T]], d: Array<T>, e: Array<[T, T]>, f: Array<T>, g: Array<Array<T>>, h: Array<Array<[T, T]>>, }"
"type Struct<T> = { a: T, b: [T, T], c: [T, [T, T]], d: [T, T, T], e: [[T, T], [T, T], [T, T]], f: Array<T>, g: Array<Array<T>>, h: Array<[[T, T], [T, T], [T, T]]>, }"
)
}

Expand Down Expand Up @@ -278,5 +278,6 @@ fn trait_bounds() {
t: [T; N],
}

assert_eq!(D::<&str, 41>::decl(), "type D<T> = { t: Array<T>, }")
let ty = format!("type D<T> = {{ t: [{}], }}", "T, ".repeat(41).trim_end_matches(", "));
assert_eq!(D::<&str, 41>::decl(), ty)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I saw the 41 and I went "naaah" lol

}
Loading