Skip to content

Commit 55cac85

Browse files
Check #[ts(flatten)] at compile time instead of panicking during export
1 parent 7182ad8 commit 55cac85

File tree

12 files changed

+148
-55
lines changed

12 files changed

+148
-55
lines changed

macros/src/deps.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ use proc_macro2::TokenStream;
44
use quote::{quote, ToTokens};
55
use syn::{Path, Type};
66

7+
#[derive(Clone, Debug)]
78
pub struct Dependencies {
89
crate_rename: Rc<Path>,
910
dependencies: HashSet<Dependency>,
1011
types: HashSet<Rc<Type>>,
1112
}
1213

13-
#[derive(Hash, Eq, PartialEq)]
14+
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
1415
enum Dependency {
1516
// A dependency on all dependencies of `ty`.
1617
// This does not include a dependency on `ty` itself - only its dependencies!

macros/src/lib.rs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct DerivedTS {
2727
inline: TokenStream,
2828
inline_flattened: Option<TokenStream>,
2929
dependencies: Dependencies,
30+
flattened_dependencies: Dependencies,
3031
concrete: HashMap<Ident, Type>,
3132
bound: Option<Vec<WherePredicate>>,
3233
ts_enum: Option<Repr>,
@@ -77,13 +78,15 @@ impl DerivedTS {
7778
};
7879

7980
let ident = self.ts_name.clone();
80-
let impl_start = generate_impl_block_header(
81+
let (impl_start, bounds) = generate_impl_block_header(
8182
&crate_rename,
8283
&rust_ty,
8384
&generics,
8485
self.bound.as_deref(),
8586
&self.dependencies,
87+
&self.flattened_dependencies,
8688
);
89+
8790
let assoc_type = generate_assoc_type(&rust_ty, &crate_rename, &generics, &self.concrete);
8891
let name = self.generate_name_fn(&generics);
8992
let is_enum = self.is_enum.clone();
@@ -92,9 +95,20 @@ impl DerivedTS {
9295
let dependencies = &self.dependencies;
9396
let generics_fn = self.generate_generics_fn(&generics);
9497

98+
let inline_flattened = self.inline_flattened.clone().map(|x| {
99+
quote! {
100+
#[automatically_derived]
101+
#impl_start #crate_rename::Flattenable #bounds {
102+
fn inline_flattened(cfg: &#crate_rename::Config) -> String {
103+
#x
104+
}
105+
}
106+
}
107+
});
108+
95109
quote! {
96110
#[automatically_derived]
97-
#impl_start {
111+
#impl_start #crate_rename::TS #bounds {
98112
#assoc_type
99113
type OptionInnerType = Self;
100114

@@ -119,6 +133,8 @@ impl DerivedTS {
119133
}
120134
}
121135

136+
#inline_flattened
137+
122138
#export
123139
}
124140
}
@@ -178,10 +194,12 @@ impl DerivedTS {
178194
type OptionInnerType = Self;
179195
fn name(cfg: &#crate_rename::Config) -> String { stringify!(#generics).to_owned() }
180196
fn inline(cfg: &#crate_rename::Config) -> String { panic!("{} cannot be inlined", #name) }
181-
fn inline_flattened(cfg: &#crate_rename::Config) -> String { stringify!(#generics).to_owned() }
182197
fn decl(cfg: &#crate_rename::Config) -> String { panic!("{} cannot be declared", #name) }
183198
fn decl_concrete(cfg: &#crate_rename::Config) -> String { panic!("{} cannot be declared", #name) }
184199
}
200+
impl #crate_rename::Flattenable for #generics {
201+
fn inline_flattened(cfg: &#crate_rename::Config) -> String { stringify!(#generics).to_owned() }
202+
}
185203
)*
186204
}
187205
}
@@ -245,12 +263,6 @@ impl DerivedTS {
245263
let inline = &self.inline;
246264
let crate_rename = &self.crate_rename;
247265

248-
let inline_flattened = self.inline_flattened.clone().unwrap_or_else(|| {
249-
quote! {
250-
panic!("{} cannot be flattened", <Self as #crate_rename::TS>::name(cfg))
251-
}
252-
});
253-
254266
let inline = match self.ts_enum {
255267
Some(Repr::Int) => quote! {
256268
let variants = #inline.replace(|x: char| !x.is_numeric() && x != ',', "");
@@ -301,10 +313,6 @@ impl DerivedTS {
301313
fn inline(cfg: &#crate_rename::Config) -> String {
302314
#inline
303315
}
304-
305-
fn inline_flattened(cfg: &#crate_rename::Config) -> String {
306-
#inline_flattened
307-
}
308316
}
309317
}
310318

@@ -394,7 +402,8 @@ fn generate_impl_block_header(
394402
generics: &Generics,
395403
bounds: Option<&[WherePredicate]>,
396404
dependencies: &Dependencies,
397-
) -> TokenStream {
405+
flattened_dependencies: &Dependencies,
406+
) -> (TokenStream, TokenStream) {
398407
use GenericParam as G;
399408

400409
let params = generics.params.iter().map(|param| match param {
@@ -426,18 +435,23 @@ fn generate_impl_block_header(
426435
let where_bound = match bounds {
427436
Some(bounds) => quote! { where #(#bounds),* },
428437
None => {
429-
let bounds = generate_where_clause(crate_rename, generics, dependencies);
438+
let bounds =
439+
generate_where_clause(crate_rename, generics, dependencies, flattened_dependencies);
430440
quote! { #bounds }
431441
}
432442
};
433443

434-
quote!(impl <#(#params),*> #crate_rename::TS for #ty <#(#type_args),*> #where_bound)
444+
(
445+
quote!(impl <#(#params),*>),
446+
quote!(for #ty <#(#type_args),*> #where_bound),
447+
)
435448
}
436449

437450
fn generate_where_clause(
438451
crate_rename: &Path,
439452
generics: &Generics,
440453
dependencies: &Dependencies,
454+
flattened_dependencies: &Dependencies,
441455
) -> WhereClause {
442456
let used_types = {
443457
let is_type_param = |id: &Ident| generics.type_params().any(|p| &p.ident == id);
@@ -449,9 +463,19 @@ fn generate_where_clause(
449463
used_types.into_iter()
450464
};
451465

466+
let flattened_used_types = {
467+
let is_type_param = |id: &Ident| generics.type_params().any(|p| &p.ident == id);
468+
469+
let mut flattened_used_types = HashSet::new();
470+
for ty in flattened_dependencies.used_types() {
471+
used_type_params(&mut flattened_used_types, ty, is_type_param);
472+
}
473+
flattened_used_types.into_iter()
474+
};
475+
452476
let existing = generics.where_clause.iter().flat_map(|w| &w.predicates);
453477
parse_quote! {
454-
where #(#existing,)* #(#used_types: #crate_rename::TS),*
478+
where #(#existing,)* #(#used_types: #crate_rename::TS),* #(, #flattened_used_types: #crate_rename::Flattenable)*
455479
}
456480
}
457481

macros/src/types/enum.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result<DerivedTS> {
3636

3737
let mut formatted_variants = Vec::new();
3838
let mut dependencies = Dependencies::new(crate_rename.clone());
39+
let mut flattened_dependencies = Dependencies::new(crate_rename.clone());
3940

4041
for variant in &s.variants {
4142
format_variant(
4243
&mut formatted_variants,
4344
&mut dependencies,
45+
&mut flattened_dependencies,
4446
&enum_attr,
4547
variant,
4648
)?;
@@ -58,6 +60,7 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result<DerivedTS> {
5860
format!("({})", [#(#formatted_variants),*].join(" | "))
5961
)),
6062
dependencies,
63+
flattened_dependencies,
6164
docs: enum_attr.docs,
6265
export: enum_attr.export,
6366
export_to: enum_attr.export_to,
@@ -72,6 +75,7 @@ pub(crate) fn r#enum_def(s: &ItemEnum) -> syn::Result<DerivedTS> {
7275
fn format_variant(
7376
formatted_variants: &mut Vec<TokenStream>,
7477
dependencies: &mut Dependencies,
78+
flattened_dependencies: &mut Dependencies,
7579
enum_attr: &EnumAttr,
7680
variant: &Variant,
7781
) -> syn::Result<()> {
@@ -123,6 +127,7 @@ fn format_variant(
123127
)?;
124128

125129
let variant_dependencies = variant_type.dependencies;
130+
let variant_flattened_dependencies = variant_type.flattened_dependencies;
126131
let inline_type = variant_type.inline;
127132

128133
let parsed_ty = match (&variant_attr.type_as, &variant_attr.type_override) {
@@ -134,6 +139,7 @@ fn format_variant(
134139
(None, Some(ty)) => quote!(#ty.to_owned()),
135140
(None, None) => {
136141
dependencies.append(variant_dependencies);
142+
flattened_dependencies.append(variant_flattened_dependencies);
137143
inline_type
138144
}
139145
};
@@ -228,7 +234,8 @@ fn empty_enum(ts_name: Expr, enum_attr: EnumAttr) -> DerivedTS {
228234
inline: quote!("never".to_owned()),
229235
docs: enum_attr.docs,
230236
inline_flattened: None,
231-
dependencies: Dependencies::new(crate_rename),
237+
dependencies: Dependencies::new(crate_rename.clone()),
238+
flattened_dependencies: Dependencies::new(crate_rename),
232239
export: enum_attr.export,
233240
export_to: enum_attr.export_to,
234241
ts_name,

macros/src/types/named.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub(crate) fn named(attr: &StructAttr, ts_name: Expr, fields: &FieldsNamed) -> R
1616
let mut formatted_fields = Vec::new();
1717
let mut flattened_fields = Vec::new();
1818
let mut dependencies = Dependencies::new(crate_rename.clone());
19+
let mut flattened_dependencies = Dependencies::new(crate_rename.clone());
1920

2021
if let Some(tag) = &attr.tag {
2122
formatted_fields.push(quote! {
@@ -29,6 +30,7 @@ pub(crate) fn named(attr: &StructAttr, ts_name: Expr, fields: &FieldsNamed) -> R
2930
&mut formatted_fields,
3031
&mut flattened_fields,
3132
&mut dependencies,
33+
&mut flattened_dependencies,
3234
field,
3335
&attr.rename_all,
3436
attr.optional_fields,
@@ -67,6 +69,7 @@ pub(crate) fn named(attr: &StructAttr, ts_name: Expr, fields: &FieldsNamed) -> R
6769
inline_flattened: Some(quote!(#inline_flattened.replace(" } & { ", " "))),
6870
docs: attr.docs.clone(),
6971
dependencies,
72+
flattened_dependencies,
7073
export: attr.export,
7174
export_to: attr.export_to.clone(),
7275
ts_name,
@@ -92,6 +95,7 @@ fn format_field(
9295
formatted_fields: &mut Vec<TokenStream>,
9396
flattened_fields: &mut Vec<TokenStream>,
9497
dependencies: &mut Dependencies,
98+
flattened_dependencies: &mut Dependencies,
9599
field: &Field,
96100
rename_all: &Option<Inflection>,
97101
struct_optional: Optional,
@@ -124,7 +128,8 @@ fn format_field(
124128
}
125129

126130
if field_attr.flatten {
127-
flattened_fields.push(quote!(<#ty as #crate_rename::TS>::inline_flattened(cfg)));
131+
flattened_fields.push(quote!(<#ty as #crate_rename::Flattenable>::inline_flattened(cfg)));
132+
flattened_dependencies.append_from(&ty);
128133
return Ok(());
129134
}
130135

macros/src/types/newtype.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub(crate) fn newtype(
4545
inline_flattened: None,
4646
docs: attr.docs.clone(),
4747
dependencies,
48+
flattened_dependencies: Dependencies::new(crate_rename.clone()),
4849
export: attr.export,
4950
export_to: attr.export_to.clone(),
5051
ts_name,

macros/src/types/tuple.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub(crate) fn tuple(attr: &StructAttr, ts_name: Expr, fields: &FieldsUnnamed) ->
2424
}
2525

2626
Ok(DerivedTS {
27-
crate_rename,
27+
crate_rename: crate_rename.clone(),
2828
inline: quote! {
2929
format!(
3030
"[{}]",
@@ -34,6 +34,7 @@ pub(crate) fn tuple(attr: &StructAttr, ts_name: Expr, fields: &FieldsUnnamed) ->
3434
inline_flattened: None,
3535
docs: attr.docs.clone(),
3636
dependencies,
37+
flattened_dependencies: Dependencies::new(crate_rename),
3738
export: attr.export,
3839
export_to: attr.export_to.clone(),
3940
ts_name,

macros/src/types/type_as.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ pub(crate) fn type_as_struct(
1717
let mut dependencies = Dependencies::new(crate_rename.clone());
1818
dependencies.append_from(type_as);
1919

20+
let flattened_dependencies = Dependencies::new(crate_rename.clone());
21+
2022
Ok(DerivedTS {
2123
crate_rename: crate_rename.clone(),
2224
inline: quote!(<#type_as as #crate_rename::TS>::inline(cfg)),
2325
inline_flattened: None,
2426
docs: attr.docs.clone(),
2527
dependencies,
28+
flattened_dependencies,
2629
export: attr.export,
2730
export_to: attr.export_to.clone(),
2831
ts_name,
@@ -39,12 +42,15 @@ pub(crate) fn type_as_enum(attr: &EnumAttr, ts_name: Expr, type_as: &Type) -> Re
3942
let mut dependencies = Dependencies::new(crate_rename.clone());
4043
dependencies.append_from(type_as);
4144

45+
let flattened_dependencies = Dependencies::new(crate_rename.clone());
46+
4247
Ok(DerivedTS {
4348
crate_rename: crate_rename.clone(),
4449
inline: quote!(<#type_as as #crate_rename::TS>::inline(cfg)),
4550
inline_flattened: None,
4651
docs: attr.docs.clone(),
4752
dependencies,
53+
flattened_dependencies,
4854
export: attr.export,
4955
export_to: attr.export_to.clone(),
5056
ts_name,

macros/src/types/type_override.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ pub(crate) fn type_override_struct(
1919
inline: quote!(#type_override.to_owned()),
2020
inline_flattened: None,
2121
docs: attr.docs.clone(),
22-
dependencies: Dependencies::new(crate_rename),
22+
dependencies: Dependencies::new(crate_rename.clone()),
23+
flattened_dependencies: Dependencies::new(crate_rename),
2324
export: attr.export,
2425
export_to: attr.export_to.clone(),
2526
ts_name,
@@ -42,7 +43,8 @@ pub(crate) fn type_override_enum(
4243
inline: quote!(#type_override.to_owned()),
4344
inline_flattened: None,
4445
docs: attr.docs.clone(),
45-
dependencies: Dependencies::new(crate_rename),
46+
dependencies: Dependencies::new(crate_rename.clone()),
47+
flattened_dependencies: Dependencies::new(crate_rename),
4648
export: attr.export,
4749
export_to: attr.export_to.clone(),
4850
ts_name,

macros/src/types/unit.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ pub(crate) fn empty_object(attr: &StructAttr, ts_name: Expr) -> DerivedTS {
1515
inline: quote!("Record<symbol, never>".to_owned()),
1616
inline_flattened: None,
1717
docs: attr.docs.clone(),
18-
dependencies: Dependencies::new(crate_rename),
18+
dependencies: Dependencies::new(crate_rename.clone()),
19+
flattened_dependencies: Dependencies::new(crate_rename),
1920
export: attr.export,
2021
export_to: attr.export_to.clone(),
2122
ts_name,
@@ -34,7 +35,8 @@ pub(crate) fn empty_array(attr: &StructAttr, ts_name: Expr) -> DerivedTS {
3435
inline: quote!("never[]".to_owned()),
3536
inline_flattened: None,
3637
docs: attr.docs.clone(),
37-
dependencies: Dependencies::new(crate_rename),
38+
dependencies: Dependencies::new(crate_rename.clone()),
39+
flattened_dependencies: Dependencies::new(crate_rename),
3840
export: attr.export,
3941
export_to: attr.export_to.clone(),
4042
ts_name,
@@ -53,7 +55,8 @@ pub(crate) fn null(attr: &StructAttr, ts_name: Expr) -> DerivedTS {
5355
inline: quote!("null".to_owned()),
5456
inline_flattened: None,
5557
docs: attr.docs.clone(),
56-
dependencies: Dependencies::new(crate_rename),
58+
dependencies: Dependencies::new(crate_rename.clone()),
59+
flattened_dependencies: Dependencies::new(crate_rename),
5760
export: attr.export,
5861
export_to: attr.export_to.clone(),
5962
ts_name,

0 commit comments

Comments
 (0)