Skip to main content

typescript_macros/
lib.rs

1#![warn(missing_docs)]
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{DeriveInput, ItemFn, parse_macro_input};
6
7/// 为 Rust 结构体生成 TypeScript 类定义
8///
9/// # 示例
10///
11/// ```rust
12/// use typescript_macros::TypescriptClass;
13///
14/// #[derive(TypescriptClass)]
15/// struct User {
16///     id: u32,
17///     name: String,
18///     active: bool,
19/// }
20/// ```
21///
22/// 这将生成对应的 TypeScript 类定义:
23///
24/// ```typescript
25/// class User {
26///     id: number;
27///     name: string;
28///     active: boolean;
29///     
30///     constructor(id: number, name: string, active: boolean) {
31///         this.id = id;
32///         this.name = name;
33///         this.active = active;
34///     }
35/// }
36/// ```
37#[proc_macro_derive(TypescriptClass, attributes(ts))]
38pub fn typescript_class_derive(input: TokenStream) -> TokenStream {
39    let input = parse_macro_input!(input as DeriveInput);
40
41    // 解析结构体名称
42    let struct_name = &input.ident;
43
44    // 解析结构体字段
45    let fields = match &input.data {
46        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields), .. }) => &fields.named,
47        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unnamed(_), .. }) => {
48            return syn::Error::new_spanned(input, "Tuple structs are not supported").to_compile_error().into();
49        }
50        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unit, .. }) => {
51            return syn::Error::new_spanned(input, "Unit structs are not supported").to_compile_error().into();
52        }
53        syn::Data::Enum(_) => {
54            return syn::Error::new_spanned(input, "Enums are not supported").to_compile_error().into();
55        }
56        syn::Data::Union(_) => {
57            return syn::Error::new_spanned(input, "Unions are not supported").to_compile_error().into();
58        }
59    };
60
61    // 检查字段是否都有名称
62    for field in fields {
63        if field.ident.is_none() {
64            return syn::Error::new_spanned(field, "All fields must have names").to_compile_error().into();
65        }
66    }
67
68    // 生成字段类型映射
69    let field_types: Vec<String> = fields
70        .iter()
71        .map(|field| {
72            let field_name = field.ident.as_ref().unwrap();
73            let field_ty = &field.ty;
74
75            // 简单的类型映射
76            let ts_type = match quote!(#field_ty).to_string().as_str() {
77                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
78                "bool" => "boolean",
79                "String" | "&str" => "string",
80                "Option<u8>" | "Option<u16>" | "Option<u32>" | "Option<u64>" | "Option<i8>" | "Option<i16>" | "Option<i32>"
81                | "Option<i64>" | "Option<f32>" | "Option<f64>" => "number | undefined",
82                "Option<bool>" => "boolean | undefined",
83                "Option<String>" | "Option<&str>" => "string | undefined",
84                _ => "any",
85            };
86
87            format!("    {}: {}", field_name, ts_type)
88        })
89        .collect();
90
91    // 生成构造函数参数
92    let constructor_params: Vec<String> = fields
93        .iter()
94        .map(|field| {
95            let field_name = field.ident.as_ref().unwrap();
96            let field_ty = &field.ty;
97
98            let ts_type = match quote!(#field_ty).to_string().as_str() {
99                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
100                "bool" => "boolean",
101                "String" | "&str" => "string",
102                "Option<u8>" | "Option<u16>" | "Option<u32>" | "Option<u64>" | "Option<i8>" | "Option<i16>" | "Option<i32>"
103                | "Option<i64>" | "Option<f32>" | "Option<f64>" => "number | undefined",
104                "Option<bool>" => "boolean | undefined",
105                "Option<String>" | "Option<&str>" => "string | undefined",
106                _ => "any",
107            };
108
109            format!("        {}: {}", field_name, ts_type)
110        })
111        .collect();
112
113    // 生成构造函数赋值
114    let constructor_assignments: Vec<String> = fields
115        .iter()
116        .map(|field| {
117            let field_name = field.ident.as_ref().unwrap();
118            format!("        this.{} = {};", field_name, field_name)
119        })
120        .collect();
121
122    // 生成 TypeScript 代码
123    let field_types_str = field_types.join(";\n");
124    let constructor_params_str = constructor_params.join(",\n");
125    let constructor_assignments_str = constructor_assignments.join("\n");
126
127    let ts_code = quote! {
128        impl #struct_name {
129            /// TypeScript 类定义
130            pub const TS_CLASS_DEFINITION: &'static str = concat!(
131                "class ", stringify!(#struct_name), " {\n",
132                #field_types_str, ";\n",
133                "\n",
134                "    constructor(\n",
135                #constructor_params_str, ",\n",
136                "    ) {\n",
137                #constructor_assignments_str, "\n",
138                "    }\n",
139                "}\n"
140            );
141
142            /// 获取 TypeScript 类定义
143            pub fn ts_class_definition() -> &'static str {
144                Self::TS_CLASS_DEFINITION
145            }
146        }
147    };
148
149    TokenStream::from(ts_code)
150}
151
152/// 为 Rust 函数生成 TypeScript 函数类型定义
153///
154/// # 示例
155///
156/// ```rust
157/// use typescript_macros::TypescriptFunction;
158///
159/// #[TypescriptFunction]
160/// fn add(a: u32, b: u32) -> u32 {
161///     a + b
162/// }
163/// ```
164///
165/// 这将生成对应的 TypeScript 函数类型定义:
166///
167/// ```typescript
168/// type AddFunction = (a: number, b: number) => number;
169/// ```
170#[proc_macro_attribute]
171pub fn TypescriptFunction(_args: TokenStream, input: TokenStream) -> TokenStream {
172    let input = parse_macro_input!(input as ItemFn);
173
174    // 解析函数名称
175    let fn_name = &input.sig.ident;
176
177    // 解析函数参数
178    let params = &input.sig.inputs;
179    let param_types: Vec<String> = params
180        .iter()
181        .map(|param| match param {
182            syn::FnArg::Typed(pat_type) => {
183                let param_name = match &*pat_type.pat {
184                    syn::Pat::Ident(pat_ident) => &pat_ident.ident,
185                    _ => return "_: any".to_string(),
186                };
187                let param_ty = &pat_type.ty;
188
189                let ts_type = match quote!(#param_ty).to_string().as_str() {
190                    "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
191                    "bool" => "boolean",
192                    "String" | "&str" => "string",
193                    "Option<u8>" | "Option<u16>" | "Option<u32>" | "Option<u64>" | "Option<i8>" | "Option<i16>"
194                    | "Option<i32>" | "Option<i64>" | "Option<f32>" | "Option<f64>" => "number | undefined",
195                    "Option<bool>" => "boolean | undefined",
196                    "Option<String>" | "Option<&str>" => "string | undefined",
197                    _ => "any",
198                };
199
200                format!("{}: {}", param_name, ts_type)
201            }
202            _ => "_: any".to_string(),
203        })
204        .collect();
205
206    // 解析返回类型
207    let return_type = match &input.sig.output {
208        syn::ReturnType::Type(_, ty) => match quote!(#ty).to_string().as_str() {
209            "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
210            "bool" => "boolean",
211            "String" | "&str" => "string",
212            "Option<u8>" | "Option<u16>" | "Option<u32>" | "Option<u64>" | "Option<i8>" | "Option<i16>" | "Option<i32>"
213            | "Option<i64>" | "Option<f32>" | "Option<f64>" => "number | undefined",
214            "Option<bool>" => "boolean | undefined",
215            "Option<String>" | "Option<&str>" => "string | undefined",
216            _ => "any",
217        },
218        syn::ReturnType::Default => "void",
219    };
220
221    // 生成 TypeScript 函数类型定义
222    let param_types_str = param_types.join(", ");
223    let ts_const_name = format!("{}_TS_FUNCTION_DEFINITION", fn_name);
224    let ts_const_ident = syn::Ident::new(&ts_const_name, fn_name.span());
225    let ts_function_name = syn::Ident::new(&format!("{}_ts_function_definition", fn_name), fn_name.span());
226
227    let ts_code = quote! {
228        #input
229
230        /// TypeScript 函数类型定义
231        pub const #ts_const_ident: &'static str = concat!(
232            "type ", stringify!(#fn_name), "Function = (",
233            #param_types_str,
234            ") => ",
235            #return_type,
236            ";\n"
237        );
238
239        /// 获取 TypeScript 函数类型定义
240        pub fn #ts_function_name() -> &'static str {
241            #ts_const_ident
242        }
243    };
244
245    TokenStream::from(ts_code)
246}
247
248/// 为 Rust 接口生成 TypeScript 接口定义
249///
250/// # 示例
251///
252/// ```rust
253/// use typescript_macros::TypescriptInterface;
254///
255/// #[derive(TypescriptInterface)]
256/// struct User {
257///     id: u32,
258///     name: String,
259///     active: bool,
260/// }
261/// ```
262///
263/// 这将生成对应的 TypeScript 接口定义:
264///
265/// ```typescript
266/// interface User {
267///     id: number;
268///     name: string;
269///     active: boolean;
270/// }
271/// ```
272#[proc_macro_derive(TypescriptInterface, attributes(ts))]
273pub fn typescript_interface_derive(input: TokenStream) -> TokenStream {
274    let input = parse_macro_input!(input as DeriveInput);
275
276    // 解析接口名称
277    let trait_name = &input.ident;
278
279    // 检查是否是 trait
280    if !matches!(input.data, syn::Data::Struct(_)) {
281        return syn::Error::new_spanned(input, "Only structs are supported for TypescriptInterface").to_compile_error().into();
282    }
283
284    // 对于结构体,我们将其视为接口
285    let fields = match &input.data {
286        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields), .. }) => &fields.named,
287        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unnamed(_), .. }) => {
288            return syn::Error::new_spanned(input, "Tuple structs are not supported").to_compile_error().into();
289        }
290        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unit, .. }) => {
291            return syn::Error::new_spanned(input, "Unit structs are not supported").to_compile_error().into();
292        }
293        _ => unreachable!(),
294    };
295
296    // 生成接口字段
297    let interface_fields: Vec<String> = fields
298        .iter()
299        .map(|field| {
300            let field_name = field.ident.as_ref().unwrap();
301            let field_ty = &field.ty;
302
303            // 简单的类型映射
304            let ts_type = match quote!(#field_ty).to_string().as_str() {
305                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
306                "bool" => "boolean",
307                "String" | "&str" => "string",
308                "Option<u8>" | "Option<u16>" | "Option<u32>" | "Option<u64>" | "Option<i8>" | "Option<i16>" | "Option<i32>"
309                | "Option<i64>" | "Option<f32>" | "Option<f64>" => "number | undefined",
310                "Option<bool>" => "boolean | undefined",
311                "Option<String>" | "Option<&str>" => "string | undefined",
312                _ => "any",
313            };
314
315            format!("    {}: {}", field_name, ts_type)
316        })
317        .collect();
318
319    // 生成 TypeScript 接口定义
320    let interface_fields_str = interface_fields.join(";\n");
321
322    let ts_code = quote! {
323        impl #trait_name {
324            /// TypeScript 接口定义
325            pub const TS_INTERFACE_DEFINITION: &'static str = concat!(
326                "interface ", stringify!(#trait_name), " {\n",
327                #interface_fields_str, ";\n",
328                "}\n"
329            );
330
331            /// 获取 TypeScript 接口定义
332            pub fn ts_interface_definition() -> &'static str {
333                Self::TS_INTERFACE_DEFINITION
334            }
335        }
336    };
337
338    TokenStream::from(ts_code)
339}
340
341/// 为 Rust 枚举生成 TypeScript 枚举定义
342///
343/// # 示例
344///
345/// ```rust
346/// use typescript_macros::TypescriptEnum;
347///
348/// #[derive(TypescriptEnum)]
349/// enum Color {
350///     Red,
351///     Green,
352///     Blue,
353/// }
354/// ```
355///
356/// 这将生成对应的 TypeScript 枚举定义:
357///
358/// ```typescript
359/// enum Color {
360///     Red = 0,
361///     Green = 1,
362///     Blue = 2,
363/// }
364/// ```
365///
366/// # 字符串枚举示例
367///
368/// ```rust
369/// use typescript_macros::TypescriptEnum;
370///
371/// #[derive(TypescriptEnum)]
372/// enum Direction {
373///     Up = "UP",
374///     Down = "DOWN",
375///     Left = "LEFT",
376///     Right = "RIGHT",
377/// }
378/// ```
379///
380/// 这将生成对应的 TypeScript 枚举定义:
381///
382/// ```typescript
383/// enum Direction {
384///     Up = "UP",
385///     Down = "DOWN",
386///     Left = "LEFT",
387///     Right = "RIGHT",
388/// }
389/// ```
390#[proc_macro_derive(TypescriptEnum, attributes(ts))]
391pub fn typescript_enum_derive(input: TokenStream) -> TokenStream {
392    let input = parse_macro_input!(input as DeriveInput);
393
394    // 解析枚举名称
395    let enum_name = &input.ident;
396
397    // 解析枚举变体
398    let variants = match &input.data {
399        syn::Data::Enum(syn::DataEnum { variants, .. }) => variants,
400        _ => {
401            return syn::Error::new_spanned(input, "Only enums are supported").to_compile_error().into();
402        }
403    };
404
405    // 生成枚举变体
406    let enum_variants: Vec<String> = variants
407        .iter()
408        .enumerate()
409        .map(|(index, variant)| {
410            let variant_name = &variant.ident;
411
412            // 检查是否有显式值
413            Ok(match &variant.fields {
414                syn::Fields::Unit => {
415                    // 无显式值,使用索引作为值
416                    format!("    {} = {}", variant_name, index)
417                }
418                syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
419                    // 有显式值
420                    let value = &fields.unnamed[0];
421                    let value_str = quote!(#value).to_string();
422                    format!("    {} = {}", variant_name, value_str)
423                }
424                _ => {
425                    return Err(syn::Error::new_spanned(
426                        variant,
427                        "Only unit variants or variants with a single value are supported",
428                    ));
429                }
430            })
431        })
432        .collect::<Result<Vec<_>, _>>()
433        .unwrap();
434
435    // 生成 TypeScript 枚举定义
436    let enum_variants_str = enum_variants.join(",\n");
437
438    let ts_code = quote! {
439        impl #enum_name {
440            /// TypeScript 枚举定义
441            pub const TS_ENUM_DEFINITION: &'static str = concat!(
442                "enum ", stringify!(#enum_name), " {\n",
443                #enum_variants_str,
444                "\n}"
445            );
446
447            /// 获取 TypeScript 枚举定义
448            pub fn ts_enum_definition() -> &'static str {
449                Self::TS_ENUM_DEFINITION
450            }
451        }
452    };
453
454    TokenStream::from(ts_code)
455}
456
457/// 为 Rust 类型定义生成 TypeScript 类型别名
458///
459/// # 示例
460///
461/// ```rust
462/// use typescript_macros::TypescriptType;
463///
464/// #[derive(TypescriptType)]
465/// struct User {
466///     id: u32,
467///     name: String,
468///     active: bool,
469/// }
470/// ```
471///
472/// 这将生成对应的 TypeScript 类型别名:
473///
474/// ```typescript
475/// type User = {
476///     id: number;
477///     name: string;
478///     active: boolean;
479/// };
480/// ```
481#[proc_macro_derive(TypescriptType, attributes(ts))]
482pub fn typescript_type_derive(input: TokenStream) -> TokenStream {
483    let input = parse_macro_input!(input as DeriveInput);
484
485    // 解析类型名称
486    let type_name = &input.ident;
487
488    // 检查是否是结构体
489    if !matches!(input.data, syn::Data::Struct(_)) {
490        return syn::Error::new_spanned(input, "Only structs are supported for TypescriptType").to_compile_error().into();
491    }
492
493    // 对于结构体,我们将其视为类型别名
494    let fields = match &input.data {
495        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields), .. }) => &fields.named,
496        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unnamed(_), .. }) => {
497            return syn::Error::new_spanned(input, "Tuple structs are not supported").to_compile_error().into();
498        }
499        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Unit, .. }) => {
500            return syn::Error::new_spanned(input, "Unit structs are not supported").to_compile_error().into();
501        }
502        _ => unreachable!(),
503    };
504
505    // 生成类型字段
506    let type_fields: Vec<String> = fields
507        .iter()
508        .map(|field| {
509            let field_name = field.ident.as_ref().unwrap();
510            let field_ty = &field.ty;
511
512            // 简单的类型映射
513            let ts_type = match quote!(#field_ty).to_string().as_str() {
514                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
515                "bool" => "boolean",
516                "String" | "&str" => "string",
517                "Option<u8>" | "Option<u16>" | "Option<u32>" | "Option<u64>" | "Option<i8>" | "Option<i16>" | "Option<i32>"
518                | "Option<i64>" | "Option<f32>" | "Option<f64>" => "number | undefined",
519                "Option<bool>" => "boolean | undefined",
520                "Option<String>" | "Option<&str>" => "string | undefined",
521                _ => "any",
522            };
523
524            format!("    {}: {}", field_name, ts_type)
525        })
526        .collect();
527
528    // 生成 TypeScript 类型别名定义
529    let type_fields_str = type_fields.join(";\n");
530
531    let ts_code = quote! {
532        impl #type_name {
533            /// TypeScript 类型别名定义
534            pub const TS_TYPE_DEFINITION: &'static str = concat!(
535                "type ", stringify!(#type_name), " = {\n",
536                #type_fields_str, ";\n",
537                "}\n"
538            );
539
540            /// 获取 TypeScript 类型别名定义
541            pub fn ts_type_definition() -> &'static str {
542                Self::TS_TYPE_DEFINITION
543            }
544        }
545    };
546
547    TokenStream::from(ts_code)
548}