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        _ => {
48            return syn::Error::new_spanned(input, "Only named fields structs are supported").to_compile_error().into();
49        }
50    };
51
52    // 生成字段类型映射
53    let field_types: Vec<String> = fields
54        .iter()
55        .map(|field| {
56            let field_name = field.ident.as_ref().unwrap();
57            let field_ty = &field.ty;
58
59            // 简单的类型映射
60            let ts_type = match quote!(#field_ty).to_string().as_str() {
61                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
62                "bool" => "boolean",
63                "String" | "&str" => "string",
64                _ => "any",
65            };
66
67            format!("    {}: {}", field_name, ts_type)
68        })
69        .collect();
70
71    // 生成构造函数参数
72    let constructor_params: Vec<String> = fields
73        .iter()
74        .map(|field| {
75            let field_name = field.ident.as_ref().unwrap();
76            let field_ty = &field.ty;
77
78            let ts_type = match quote!(#field_ty).to_string().as_str() {
79                "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
80                "bool" => "boolean",
81                "String" | "&str" => "string",
82                _ => "any",
83            };
84
85            format!("        {}: {}", field_name, ts_type)
86        })
87        .collect();
88
89    // 生成构造函数赋值
90    let constructor_assignments: Vec<String> = fields
91        .iter()
92        .map(|field| {
93            let field_name = field.ident.as_ref().unwrap();
94            format!("        this.{} = {};", field_name, field_name)
95        })
96        .collect();
97
98    // 生成 TypeScript 代码
99    let field_types_str = field_types.join(";\n");
100    let constructor_params_str = constructor_params.join(",\n");
101    let constructor_assignments_str = constructor_assignments.join("\n");
102
103    let ts_code = quote! {
104        impl #struct_name {
105            /// TypeScript 类定义
106            pub const TS_CLASS_DEFINITION: &'static str = concat!(
107                "class ", stringify!(#struct_name), " {\n",
108                #field_types_str, ";\n",
109                "\n",
110                "    constructor(\n",
111                #constructor_params_str, ",\n",
112                "    ) {\n",
113                #constructor_assignments_str, "\n",
114                "    }\n",
115                "}\n"
116            );
117        }
118    };
119
120    TokenStream::from(ts_code)
121}
122
123/// 为 Rust 函数生成 TypeScript 函数类型定义
124///
125/// # 示例
126///
127/// ```rust
128/// use typescript_macros::TypescriptFunction;
129///
130/// #[TypescriptFunction]
131/// fn add(a: u32, b: u32) -> u32 {
132///     a + b
133/// }
134/// ```
135///
136/// 这将生成对应的 TypeScript 函数类型定义:
137///
138/// ```typescript
139/// type AddFunction = (a: number, b: number) => number;
140/// ```
141#[proc_macro_attribute]
142pub fn TypescriptFunction(_args: TokenStream, input: TokenStream) -> TokenStream {
143    let input = parse_macro_input!(input as ItemFn);
144
145    // 解析函数名称
146    let fn_name = &input.sig.ident;
147
148    // 解析函数参数
149    let params = &input.sig.inputs;
150    let param_types: Vec<String> = params
151        .iter()
152        .map(|param| match param {
153            syn::FnArg::Typed(pat_type) => {
154                let param_name = match &*pat_type.pat {
155                    syn::Pat::Ident(pat_ident) => &pat_ident.ident,
156                    _ => return "_: any".to_string(),
157                };
158                let param_ty = &pat_type.ty;
159
160                let ts_type = match quote!(#param_ty).to_string().as_str() {
161                    "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
162                    "bool" => "boolean",
163                    "String" | "&str" => "string",
164                    _ => "any",
165                };
166
167                format!("{}: {}", param_name, ts_type)
168            }
169            _ => "_: any".to_string(),
170        })
171        .collect();
172
173    // 解析返回类型
174    let return_type = match &input.sig.output {
175        syn::ReturnType::Type(_, ty) => match quote!(#ty).to_string().as_str() {
176            "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64" | "f32" | "f64" => "number",
177            "bool" => "boolean",
178            "String" | "&str" => "string",
179            _ => "any",
180        },
181        syn::ReturnType::Default => "void",
182    };
183
184    // 生成 TypeScript 函数类型定义
185    let param_types_str = param_types.join(", ");
186    let ts_const_name = format!("{}_TS_FUNCTION_DEFINITION", fn_name);
187    let ts_const_ident = syn::Ident::new(&ts_const_name, fn_name.span());
188
189    let ts_code = quote! {
190        #input
191
192        /// TypeScript 函数类型定义
193        pub const #ts_const_ident: &'static str = concat!(
194            "type ", stringify!(#fn_name), "Function = (",
195            #param_types_str,
196            ") => ",
197            #return_type,
198            ";\n"
199        );
200    };
201
202    TokenStream::from(ts_code)
203}