Skip to content

Commit e2e78ed

Browse files
authored
Support generics (#17)
* Support generics * Bump to 0.10
1 parent 7399572 commit e2e78ed

File tree

8 files changed

+230
-44
lines changed

8 files changed

+230
-44
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
authors = ["ImJeremyHe<[email protected]>"]
33
edition = "2018"
44
name = "gents"
5-
version = "0.9.2"
5+
version = "0.10.0"
66
license = "MIT"
77
description = "generate Typescript interfaces from Rust code"
88
repository = "https://github.com/ImJeremyHe/gents"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ You should import `gents` in your Cargo.toml.
4343

4444
```toml
4545
[dev-dependencies]
46-
gents = "0.4"
47-
gents_derives = "0.4"
46+
gents = "0.10"
47+
gents_derives = "0.10"
4848
```
4949

5050
```rust

derives/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "gents_derives"
3-
version = "0.9.2"
3+
version = "0.10.0"
44
description = "provides some macros for gents"
55
authors = ["ImJeremyHe<[email protected]>"]
66
license = "MIT"

derives/src/container.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct Container<'a> {
2222
pub ident: &'a Ident,
2323
pub comments: Vec<String>,
2424
pub need_builder: bool,
25+
pub generics: Vec<&'a Ident>,
2526
pub tag: Option<String>,
2627
}
2728

@@ -32,6 +33,15 @@ impl<'a> Container<'a> {
3233
let mut rename: Option<String> = None;
3334
let mut need_builder = false;
3435
let mut tag: Option<String> = None;
36+
let generics = item
37+
.generics
38+
.params
39+
.iter()
40+
.filter_map(|p| match p {
41+
syn::GenericParam::Type(ty_param) => Some(&ty_param.ident),
42+
_ => None,
43+
})
44+
.collect::<Vec<_>>();
3545
let comments = parse_comments(&item.attrs);
3646
for meta_item in item
3747
.attrs
@@ -89,6 +99,7 @@ impl<'a> Container<'a> {
8999
rename,
90100
comments,
91101
need_builder,
102+
generics,
92103
tag,
93104
}
94105
}
@@ -111,6 +122,7 @@ impl<'a> Container<'a> {
111122
comments,
112123
need_builder,
113124
tag,
125+
generics,
114126
}
115127
}
116128
_ => panic!("gents does not support the union type currently, use struct instead"),

derives/src/lib.rs

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use case::{convert_camel_from_pascal, convert_camel_from_snake};
2+
use proc_macro2::Span;
23
use syn::{parse_macro_input, DeriveInput};
34
mod case;
45
mod container;
@@ -43,8 +44,8 @@ fn get_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
4344
let fields = container.fields;
4445
let rename = container.rename;
4546
let ts_name = match rename {
46-
Some(s) => s,
47-
None => ident.to_string(),
47+
Some(s) if is_enum => s,
48+
_ => ident.to_string(),
4849
};
4950
let comments = container.comments;
5051
let need_builder = container.need_builder;
@@ -54,6 +55,32 @@ fn get_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
5455
"".to_string()
5556
};
5657
let register_func = {
58+
let generics_dep_register = container.generics.iter().map(|g| {
59+
quote! {
60+
<#g as ::gents::TS>::_register(manager, true);
61+
}
62+
});
63+
let generic_register = if !container.generics.is_empty() {
64+
let placehoders = container
65+
.generics
66+
.iter()
67+
.map(|g| syn::Ident::new(&format!("{}_{}", ident, g), Span::call_site()));
68+
let _placeholders = placehoders.clone();
69+
let placeholders_str = container
70+
.generics
71+
.iter()
72+
.map(|g| g.to_string())
73+
.collect::<Vec<_>>()
74+
.join(", ");
75+
quote! {
76+
let _d = <#ident<#(#placehoders),*> as ::gents::TS>::_register(manager, false);
77+
deps.push(_d);
78+
manager.add_generics_map(_d, #placeholders_str.to_string());
79+
generic = Some(_d);
80+
}
81+
} else {
82+
quote! {}
83+
};
5784
let field_ds = fields.into_iter().filter(|f| !f.skip).map(|s| {
5885
let fi = s.ident;
5986
let rename = s.rename;
@@ -78,7 +105,7 @@ fn get_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
78105
};
79106
if let Some(ty) = ty {
80107
quote! {
81-
let dep = <#ty as ::gents::TS>::_register(manager);
108+
let dep = <#ty as ::gents::TS>::_register(manager, true);
82109
deps.push(dep);
83110
let fd = ::gents::FieldDescriptor {
84111
ident: #name.to_string(),
@@ -111,6 +138,7 @@ fn get_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
111138
ts_name: #ts_name.to_string(),
112139
comments: vec![#(#comments.to_string()),*],
113140
tag: #tag.to_string(),
141+
generic,
114142
};
115143
let descriptor = ::gents::Descriptor::Enum(_enum);
116144
}
@@ -123,31 +151,102 @@ fn get_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
123151
ts_name: #ts_name.to_string(),
124152
comments: vec![#(#comments.to_string()),*],
125153
need_builder: #need_builder,
154+
generic,
126155
};
127156
let descriptor = ::gents::Descriptor::Interface(_interface);
128157
}
129158
};
130159
quote! {
131-
fn _register(manager: &mut ::gents::DescriptorManager) -> usize {
160+
fn _register(manager: &mut ::gents::DescriptorManager, generic_base: bool) -> usize {
132161
let type_id = std::any::TypeId::of::<Self>();
133162
let mut deps = ::std::vec::Vec::<usize>::new();
134163
let mut fields = ::std::vec::Vec::<::gents::FieldDescriptor>::new();
164+
let mut generic = None;
165+
if generic_base {
166+
#generic_register
167+
}
135168
#(#field_ds)*
169+
#(#generics_dep_register)*
136170
#descriptor
137171
manager.registry(type_id, descriptor)
138172
}
139173
}
140174
};
141-
let ts_name_func = quote! {
142-
fn _ts_name() -> String {
143-
#ts_name.to_string()
175+
let generics = container
176+
.generics
177+
.iter()
178+
.map(|g| quote! {generics_names.push(<#g as ::gents::TS>::_ts_name())});
179+
let ts_name_func = if container.generics.is_empty() {
180+
quote! {
181+
fn _ts_name() -> String {
182+
#ts_name.to_string()
183+
}
184+
}
185+
} else {
186+
quote! {
187+
fn _ts_name() -> String {
188+
let name = #ts_name;
189+
let mut generics_names = Vec::<String>::new();
190+
#(#generics;)*
191+
let generics = generics_names.join(", ");
192+
format!("{}<{}>", name, generics)
193+
}
144194
}
145195
};
196+
if container.generics.is_empty() {
197+
quote! {
198+
#[cfg(any(test, feature="gents"))]
199+
impl ::gents::TS for #ident {
200+
#register_func
201+
#ts_name_func
202+
}
203+
}
204+
} else {
205+
let generics_ts = container.generics.iter().map(|g| {
206+
quote! {
207+
#g: ::gents::TS + 'static
208+
}
209+
});
210+
let generics_idents = &container.generics;
211+
let placeholder_impls = generics_idents
212+
.iter()
213+
.map(|g| get_generic_placeholder(&ident, g));
214+
quote! {
215+
#(#placeholder_impls)*
216+
#[cfg(any(test, feature="gents"))]
217+
impl<#(#generics_ts),*>
218+
::gents::TS for #ident<#(#generics_idents),*>{
219+
#register_func
220+
#ts_name_func
221+
}
222+
}
223+
}
224+
}
225+
226+
fn get_generic_placeholder(
227+
parent_ident: &syn::Ident,
228+
placeholder: &syn::Ident,
229+
) -> proc_macro2::TokenStream {
230+
let tag_ident = syn::Ident::new(
231+
&format!("{}_{}", parent_ident, placeholder),
232+
Span::call_site(),
233+
);
234+
let ts_name = format!("{}", placeholder);
146235
quote! {
147236
#[cfg(any(test, feature="gents"))]
148-
impl ::gents::TS for #ident {
149-
#register_func
150-
#ts_name_func
237+
struct #tag_ident;
238+
#[cfg(any(test, feature="gents"))]
239+
impl ::gents::TS for #tag_ident {
240+
fn _register(manager: &mut ::gents::DescriptorManager, _generic_base: bool) -> usize {
241+
let type_id = std::any::TypeId::of::<Self>();
242+
let descriptor = ::gents::BuiltinTypeDescriptor {
243+
ts_name: #ts_name.to_string(),
244+
};
245+
manager.registry(type_id, ::gents::Descriptor::BuiltinType(descriptor))
246+
}
247+
fn _ts_name() -> String {
248+
#ts_name.to_string()
249+
}
151250
}
152251
}
153252
}

src/descriptor.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::utils::remove_ext;
99
// `TS` trait defines the behavior of your types when generating files.
1010
// `TS` generates some helper functions for file generator.
1111
pub trait TS {
12-
fn _register(manager: &mut DescriptorManager) -> usize;
12+
fn _register(manager: &mut DescriptorManager, generic_base: bool) -> usize;
1313
// The name of this Rust type in Typescript.
1414
// u8 -> number
1515
// f64 -> number
@@ -30,6 +30,7 @@ pub struct DescriptorManager {
3030
pub descriptors: Vec<Descriptor>,
3131
pub id_map: HashMap<TypeId, usize>,
3232
tag_map: HashMap<usize, HashMap<String, HashSet<String>>>,
33+
generics_map: HashMap<usize, String>,
3334
}
3435

3536
impl DescriptorManager {
@@ -65,6 +66,10 @@ impl DescriptorManager {
6566
}
6667
}
6768

69+
pub fn add_generics_map(&mut self, idx: usize, generics: String) {
70+
self.generics_map.insert(idx, generics);
71+
}
72+
6873
pub fn gen_data(self) -> Vec<(String, String)> {
6974
let mut result: Vec<(String, String)> = vec![];
7075
let descriptors = &self.descriptors;
@@ -73,7 +78,15 @@ impl DescriptorManager {
7378
.enumerate()
7479
.for_each(|(idx, descriptor)| match descriptor {
7580
Descriptor::Interface(d) => {
81+
if d.generic.is_some() {
82+
return;
83+
}
7684
let tag_map = self.tag_map.get(&idx).map(|v| v.iter().collect::<Vec<_>>());
85+
let generics = if let Some(v) = self.generics_map.get(&idx) {
86+
format!("<{}>", v).to_string()
87+
} else {
88+
String::new()
89+
};
7790
let tag_content = {
7891
if let Some(tag_map) = tag_map.clone() {
7992
match tag_map.len() {
@@ -140,10 +153,11 @@ impl DescriptorManager {
140153
});
141154
let fields_string = fields_strings.join("\n");
142155
let mut content = format!(
143-
"{}\n{}export interface {} {{\n{}{}\n}}\n",
156+
"{}\n{}export interface {}{} {{\n{}{}\n}}\n",
144157
import_string,
145158
comments,
146159
d.ts_name.to_string(),
160+
generics,
147161
tag_content,
148162
fields_string
149163
);
@@ -156,6 +170,9 @@ impl DescriptorManager {
156170
result.push((d.file_name.to_string(), format(content)))
157171
}
158172
Descriptor::Enum(e) => {
173+
if e.generic.is_some() {
174+
return;
175+
}
159176
let import_deps =
160177
e.dependencies
161178
.iter()
@@ -252,6 +269,7 @@ pub struct EnumDescriptor {
252269
pub ts_name: String,
253270
pub comments: Vec<String>,
254271
pub tag: String,
272+
pub generic: Option<usize>,
255273
}
256274

257275
/// Describe how to generate a ts interface.
@@ -264,6 +282,7 @@ pub struct InterfaceDescriptor {
264282
pub ts_name: String,
265283
pub comments: Vec<String>,
266284
pub need_builder: bool,
285+
pub generic: Option<usize>,
267286
}
268287

269288
#[derive(Debug)]
@@ -278,7 +297,7 @@ pub struct FieldDescriptor {
278297
macro_rules! impl_builtin {
279298
($i: ident, $l: literal, $t: literal) => {
280299
impl TS for $i {
281-
fn _register(manager: &mut DescriptorManager) -> usize {
300+
fn _register(manager: &mut DescriptorManager, _generic_base: bool) -> usize {
282301
let type_id = TypeId::of::<$i>();
283302
let descriptor = BuiltinTypeDescriptor {
284303
ts_name: $l.to_string(),
@@ -311,8 +330,8 @@ impl_builtin!(String, "string", "string");
311330
impl_builtin!(bool, "boolean", "bool");
312331

313332
impl<T: TS + 'static> TS for Vec<T> {
314-
fn _register(manager: &mut DescriptorManager) -> usize {
315-
let idx = T::_register(manager);
333+
fn _register(manager: &mut DescriptorManager, generic_base: bool) -> usize {
334+
let idx = T::_register(manager, generic_base);
316335
let type_id = TypeId::of::<Self>();
317336
let descriptor = GenericDescriptor {
318337
dependencies: vec![idx],
@@ -333,8 +352,8 @@ impl<T: TS + 'static> TS for Vec<T> {
333352
}
334353

335354
impl<T: TS + 'static> TS for Option<T> {
336-
fn _register(manager: &mut DescriptorManager) -> usize {
337-
let idx = T::_register(manager);
355+
fn _register(manager: &mut DescriptorManager, generic_base: bool) -> usize {
356+
let idx = T::_register(manager, generic_base);
338357
let type_id = TypeId::of::<Self>();
339358
let descriptor = GenericDescriptor {
340359
dependencies: vec![idx],
@@ -354,9 +373,9 @@ impl<T: TS + 'static> TS for Option<T> {
354373
}
355374

356375
impl<T: TS + 'static, E: TS + 'static> TS for Result<T, E> {
357-
fn _register(manager: &mut DescriptorManager) -> usize {
358-
let t_idx = T::_register(manager);
359-
let e_idx = E::_register(manager);
376+
fn _register(manager: &mut DescriptorManager, generic_base: bool) -> usize {
377+
let t_idx = T::_register(manager, generic_base);
378+
let e_idx = E::_register(manager, generic_base);
360379
let type_id = TypeId::of::<Self>();
361380
let descriptor = GenericDescriptor {
362381
dependencies: vec![t_idx, e_idx],
@@ -376,9 +395,9 @@ where
376395
K: TS + 'static,
377396
V: TS + 'static,
378397
{
379-
fn _register(manager: &mut DescriptorManager) -> usize {
380-
let k_dep = K::_register(manager);
381-
let v_dep = V::_register(manager);
398+
fn _register(manager: &mut DescriptorManager, generic_base: bool) -> usize {
399+
let k_dep = K::_register(manager, generic_base);
400+
let v_dep = V::_register(manager, generic_base);
382401
let descriptor = GenericDescriptor {
383402
dependencies: vec![k_dep, v_dep],
384403
ts_name: Self::_ts_name(),
@@ -398,9 +417,9 @@ where
398417
K: TS + 'static,
399418
V: TS + 'static,
400419
{
401-
fn _register(manager: &mut DescriptorManager) -> usize {
402-
let k_dep = K::_register(manager);
403-
let v_dep = V::_register(manager);
420+
fn _register(manager: &mut DescriptorManager, generic_base: bool) -> usize {
421+
let k_dep = K::_register(manager, generic_base);
422+
let v_dep = V::_register(manager, generic_base);
404423
let descriptor = GenericDescriptor {
405424
dependencies: vec![k_dep, v_dep],
406425
ts_name: Self::_ts_name(),

0 commit comments

Comments
 (0)