1use case::{convert_camel_from_pascal, convert_camel_from_snake};
2use syn::{parse_macro_input, DeriveInput};
3mod case;
4mod container;
5mod symbol;
6
7use container::{Container, RenameAll};
8use proc_macro::TokenStream;
9use quote::quote;
10
11use crate::container::GentsWasmAttrs;
12
13#[proc_macro_attribute]
14pub fn gents_header(attr: TokenStream, item: TokenStream) -> TokenStream {
15 let item: proc_macro2::TokenStream = item.into();
16 let attrs = syn::parse2::<GentsWasmAttrs>(attr.into()).expect("parse error, please check");
17 let file_name = attrs.get_file_name();
18 quote! {
19 #[derive(::serde::Serialize, ::serde::Deserialize)]
20 #[cfg_attr(any(test, feature = "gents"), derive(::gents_derives::TS))]
21 #[cfg_attr(
22 any(test, feature = "gents"),
23 ts(file_name = #file_name, rename_all = "camelCase")
24 )]
25 #[serde(rename_all = "camelCase")]
26 #item
27 }
28 .into()
29}
30
31#[proc_macro_derive(TS, attributes(ts))]
32pub fn derive_ts(input: TokenStream) -> TokenStream {
33 let input = parse_macro_input!(input as DeriveInput);
34 get_impl_block(input).into()
35}
36
37fn get_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
38 let container = Container::from_ast(&input);
39 let file_name = container.file_name;
40 let is_enum = container.is_enum;
41 let rename_all = container.rename_all;
42 let ident = container.ident;
43 let fields = container.fields;
44 let rename = container.rename;
45 let ts_name = match rename {
46 Some(s) => s,
47 None => ident.to_string(),
48 };
49 let comments = container.comments;
50 let register_func = {
51 let field_ds = fields.into_iter().filter(|f| !f.skip).map(|s| {
52 let fi = s.ident;
53 let rename = s.rename;
54 let ty = s.ty;
55 let field_comments = s.comments;
56 let name = match (rename, &rename_all) {
57 (None, None) => fi.to_string(),
58 (None, Some(RenameAll::CamelCase)) => {
59 let s = fi.to_string();
60 if is_enum {
61 convert_camel_from_pascal(s)
62 } else {
63 convert_camel_from_snake(s)
64 }
65 }
66 (Some(s), _) => s,
67 };
68 if let Some(ty) = ty {
69 quote! {
70 let dep = <#ty as ::gents::TS>::_register(manager);
71 deps.insert(dep);
72 let fd = ::gents::FieldDescriptor {
73 ident: #name.to_string(),
74 optional: <#ty as ::gents::TS>::_is_optional(),
75 ts_ty: <#ty as ::gents::TS>::_ts_name(),
76 comments: vec![#(#field_comments.to_string()),*],
77 };
78 fields.push(fd);
79 }
80 } else {
81 quote! {
82 let fd = ::gents::FieldDescriptor {
83 ident: #name.to_string(),
84 optional: false,
85 ts_ty: String::from(""),
86 comments: vec![#(#field_comments.to_string()),*],
87 };
88 fields.push(fd);
89 }
90 }
91 });
92 let descriptor = if is_enum {
93 quote! {
94 let _enum = ::gents::EnumDescriptor {
95 dependencies: deps.into_iter().collect(),
96 fields,
97 file_name: #file_name.to_string(),
98 ts_name: #ts_name.to_string(),
99 comments: vec![#(#comments.to_string()),*],
100 };
101 let descriptor = ::gents::Descriptor::Enum(_enum);
102 }
103 } else {
104 quote! {
105 let deps_vec = deps.into_iter().collect();
106 let _interface = ::gents::InterfaceDescriptor {
107 dependencies: deps_vec,
108 fields,
109 file_name: #file_name.to_string(),
110 ts_name: #ts_name.to_string(),
111 comments: vec![#(#comments.to_string()),*],
112 };
113 let descriptor = ::gents::Descriptor::Interface(_interface);
114 }
115 };
116 quote! {
117 fn _register(manager: &mut ::gents::DescriptorManager) -> usize {
118 let type_id = std::any::TypeId::of::<Self>();
119 let mut deps = ::std::collections::HashSet::<usize>::new();
120 let mut fields = vec![];
121 #(#field_ds)*
122 #descriptor
123 manager.registry(type_id, descriptor)
124 }
125 }
126 };
127 let ts_name_func = quote! {
128 fn _ts_name() -> String {
129 #ts_name.to_string()
130 }
131 };
132 quote! {
133 #[cfg(any(test, feature="gents"))]
134 impl ::gents::TS for #ident {
135 #register_func
136 #ts_name_func
137 }
138 }
139}