Skip to main content

robotech_macros/
lib.rs

1mod dao;
2mod db;
3mod dto;
4mod feign;
5mod log;
6mod svc;
7mod vo;
8mod web;
9
10use crate::dao::{dao_macro, DaoArgs};
11use crate::db::MigrateArgs;
12use crate::dto::{crud_dto_macro, CrudDtoArgs};
13use crate::feign::feign_macro;
14use crate::log::{log_call_macro, LogCallArgs};
15use crate::svc::{db_unwrap_macro, svc_macro, DbUnwrapArgs};
16use crate::vo::{vo_macro, VoArgs};
17use crate::web::{api_doc_macro, ctrl_macro, router_macro, ApiDocArgs, RouterArgs};
18use proc_macro::TokenStream;
19use syn::{parse_macro_input, DeriveInput, ItemFn, ItemStruct};
20
21/// 属性宏:在进入方法时使用 log 库记录方法名、参数及参数值
22///
23/// # 使用示例
24/// ```
25/// // 使用默认 debug 级别
26/// #[log_call]
27/// fn add(a: i32, b: i32) -> i32 {
28///     a + b
29/// }
30///
31/// // 指定日志级别
32/// #[log_call(level = info)]
33/// fn process(data: &str) {
34///     // ...
35/// }
36///
37/// #[log_call(level = warn)]
38/// fn risky_operation() {
39///     // ...
40/// }
41/// ```
42///
43/// 支持的日志级别: trace, debug (默认), info, warn, error
44#[proc_macro_attribute]
45pub fn log_call(args: TokenStream, input: TokenStream) -> TokenStream {
46    // 解析属性参数
47    let args = parse_macro_input!(args as LogCallArgs);
48    let input = parse_macro_input!(input as ItemFn);
49    log_call_macro(args, input).into()
50}
51
52#[proc_macro]
53pub fn db_migrate(args: TokenStream) -> TokenStream {
54    let args = parse_macro_input!(args as MigrateArgs);
55    db::db_migrate_macro(args).into()
56}
57
58/// 属性宏:为XxxDto结构体自动生成XxxAddDto、XxxModifyDto、XxxSaveDto
59///
60/// # 使用示例
61/// ```
62/// #[crud_dto]
63/// pub struct OssBucketDto {
64///     /// 名称
65///     pub name: String,
66///     /// 备注
67///     pub remark: Option<String>,
68/// }
69/// ```
70///
71/// 上述代码会被展开为三个结构体:
72/// - OssBucketAddDto(带验证)
73/// - OssBucketModifyDto(不带验证)
74/// - OssBucketSaveDto(不带验证)
75#[proc_macro_attribute]
76pub fn crud_dto(args: TokenStream, input: TokenStream) -> TokenStream {
77    let args = parse_macro_input!(args as CrudDtoArgs);
78    let input = parse_macro_input!(input as ItemStruct);
79    crud_dto_macro(args, input).into()
80}
81
82/// 属性宏:为 VO 结构体自动生成标准属性
83///
84/// 此宏会自动为 VO 结构体添加以下属性和派生宏:
85/// - `#[skip_serializing_none]` - 跳过空字段序列化
86/// - `#[derive(o2o, ToSchema, Debug, Serialize, Clone)]` - 必要的派生宏
87/// - `#[from_owned(Model)]` - o2o 转换配置
88/// - `#[serde(rename_all = "camelCase")]` - 驼峰命名
89/// - `#[serde_as]` - serde_with 支持
90///
91/// 同时会自动为无符号整型字段添加:
92/// - `#[from(~ as u64)]` 或 `#[from(~.to_string())]` - 根据字段名自动判断
93/// - `#[serde_as(as = "String")]` - 避免 JS 精度丢失
94///
95/// # 使用示例
96/// ```
97/// #[vo]
98/// pub struct StudentVo {
99///     /// ID
100///     pub id: u64,
101///     /// 名称
102///     pub name: String,
103///     /// 备注
104///     pub remark: Option<String>,
105/// }
106/// ```
107///
108/// 上述代码会被展开为:
109/// ```
110/// #[skip_serializing_none]
111/// #[derive(o2o, ToSchema, Debug, Serialize, Clone)]
112/// #[from_owned(Model)]
113/// #[serde(rename_all = "camelCase")]
114/// #[serde_as]
115/// pub struct StudentVo {
116///     /// ID
117///     #[from(~ as u64)]
118///     #[serde_as(as = "String")]
119///     pub id: u64,
120///     /// 名称
121///     pub name: String,
122///     /// 备注
123///     pub remark: Option<String>,
124/// }
125/// ```
126#[proc_macro_attribute]
127pub fn vo(args: TokenStream, input: TokenStream) -> TokenStream {
128    let args = parse_macro_input!(args as VoArgs);
129    let input = parse_macro_input!(input as DeriveInput);
130    vo_macro(args, input).into()
131}
132
133/// 属性宏:为DAO结构体生成标准的CRUD方法
134///
135/// # 使用示例
136/// ```
137/// // 生成所有方法
138/// #[dao(all)]
139/// pub struct MyDao;
140///
141/// // 选择性生成方法
142/// #[dao(insert, update, get_by_id)]
143/// pub struct MyDao;
144///
145/// // 只生成查询方法
146/// #[dao(get_by_id)]
147/// pub struct MyDao;
148/// ```
149///
150/// 支持的方法选项:
151/// - insert: 生成插入方法
152/// - update: 生成更新方法
153/// - delete: 生成删除方法
154/// - get_by_id: 生成根据ID查询方法
155/// - all: 生成所有方法
156#[proc_macro_attribute]
157pub fn dao(args: TokenStream, input: TokenStream) -> TokenStream {
158    let args = parse_macro_input!(args as DaoArgs);
159    let input = parse_macro_input!(input as ItemStruct);
160    dao_macro(args, input).into()
161}
162
163/// 属性宏:为Service查询方法生成标准结构
164///
165/// 此宏会自动处理数据库连接逻辑,用户只需编写返回语句
166///
167/// # 使用示例
168/// ```
169/// #[db_unwrap]
170/// pub async fn get_by_name<C>(name: &str, db: Option<&C>) -> Result<Ro<OssBucketVo>, SvcError>
171/// where
172///     C: ConnectionTrait,
173/// {
174///     let one = OssBucketDao::get_by_name(name, db).await?;
175///     Ok(
176///         Ro::success("查询成功".to_string())
177///             .extra(one.map(|value| OssBucketVo::from(value))),
178///     )
179/// }
180/// ```
181/// 注意:用户代码中应该包含完整的返回逻辑
182#[proc_macro_attribute]
183pub fn db_unwrap(args: TokenStream, input: TokenStream) -> TokenStream {
184    let args = parse_macro_input!(args as DbUnwrapArgs);
185    let input = parse_macro_input!(input as ItemFn);
186    db_unwrap_macro(args, input).into()
187}
188
189#[proc_macro_attribute]
190pub fn svc(_args: TokenStream, input: TokenStream) -> TokenStream {
191    let input = parse_macro_input!(input as ItemStruct);
192    svc_macro(input).into()
193}
194
195#[proc_macro_attribute]
196pub fn ctrl(_args: TokenStream, input: TokenStream) -> TokenStream {
197    let input = parse_macro_input!(input as ItemStruct);
198    ctrl_macro(input).into()
199}
200
201#[proc_macro_attribute]
202pub fn router(args: TokenStream, input: TokenStream) -> TokenStream {
203    let args = parse_macro_input!(args as RouterArgs);
204    let input = parse_macro_input!(input as ItemStruct);
205    router_macro(args, input).into()
206}
207
208#[proc_macro_attribute]
209pub fn api_doc(args: TokenStream, input: TokenStream) -> TokenStream {
210    let args = parse_macro_input!(args as ApiDocArgs);
211    let input = parse_macro_input!(input as ItemStruct);
212    api_doc_macro(args, input).into()
213}
214
215/// 属性宏:为 Feign API 客户端结构体自动生成 `build_headers` 辅助方法
216///
217/// 该宏会为包装了 `FeignApiClient` 的结构体生成一个 `build_headers` 方法,
218/// 用于构建包含当前用户 ID 的请求头,避免在每个方法中重复编写 header 构建逻辑。
219///
220/// # 使用示例
221/// ```
222/// use robotech::macros::feign_client;
223///
224/// #[feign_client]
225/// pub struct OssFileApiClient {
226///     client: FeignApiClient,
227/// }
228/// ```
229///
230/// 展开后会生成:
231/// ```
232/// impl OssFileApiClient {
233///     fn build_headers(
234///         current_user_id: u64,
235///     ) -> Result<HeaderMap, ApiClientError> {
236///         // ... 构建包含 USER_ID_HEADER_NAME 的 HeaderMap
237///     }
238/// }
239/// ```
240#[proc_macro_attribute]
241pub fn feign(_args: TokenStream, input: TokenStream) -> TokenStream {
242    let input = parse_macro_input!(input as ItemStruct);
243    feign_macro(input).into()
244}