Skip to main content

next_web_macros/
lib.rs

1extern crate proc_macro;
2
3use crate::data::builder::impl_macro_builder;
4use crate::data::constructor::impl_macro_required_args_constructor;
5use crate::data::field_name::impl_macro_field_name;
6use crate::data::get_set::impl_macro_get_set;
7use crate::web::idempotency::impl_macro_idempotency;
8use crate::web::pre_authorize::impl_macro_pre_authorize;
9use crate::web::properties::impl_macro_properties;
10
11use data::desensitized::impl_macro_desensitized;
12use proc_macro::TokenStream;
13use syn::parse_macro_input;
14use syn::DeriveInput;
15use syn::ItemFn;
16use syn::ItemStruct;
17
18mod data;
19mod util;
20mod web;
21
22/// 为结构体自动生成 getter 和 setter 方法。
23///
24/// 该宏会为结构体的每个字段生成对应的 `get_{field}` 和 `set_{field}` 方法,
25/// 除非字段上使用了 `#[get_set(...)]` 属性进行跳过控制。
26///
27/// # 字段属性控制
28///
29/// - `#[get_set(skip)]`:跳过该字段的 getter 和 setter 生成。
30/// - `#[get_set(skip_get)]`:仅跳过 getter 方法的生成。
31/// - `#[get_set(skip_set)]`:仅跳过 setter 方法的生成。
32///
33/// # 示例
34///
35/// ```rust
36/// #[derive(GetSet)]
37/// struct MyStruct {
38///     #[get_set(skip)]
39///     field1: i32,        // 不生成任何方法
40///     field2: i32,        // 生成 get_field2() 和 set_field2()
41///     #[get_set(skip_get)]
42///     field3: i32,        // 仅生成 set_field3()
43///     #[get_set(skip_set)]
44///     field4: i32,        // 仅生成 get_field4()
45/// }
46/// ```
47///
48/// Automatically generate getter and setter methods for structures.
49///
50/// This macro will generate corresponding 'get_ {field}' and 'set_ {field}' methods for each field of the structure,
51/// Unless the '# [get_det (...)' attribute is used on the field for skip control.
52///
53/// # Field attribute control
54///
55/// - ` # [get_det (skip)] `: Skip the generator and setter generation for this field.
56/// - ` # [get_det (skip_get)] `: Only skip the generation of the getter method.
57/// - ` # [get_det (skip_set)] `: Skip only the generation of setter methods.
58///
59/// # Example
60///
61/// ```rust
62/// #[derive(GetSet)]
63/// struct MyStruct {
64///     #[get_set(skip)]
65///     field1: i32, //Do not generate any methods
66///     field2: i32, //Generate get_field2() and set_field2()
67///     #[get_set(skip_get)]
68///     field3: i32, //Only generate set_field3()
69///     #[get_set(skip_set)]
70///     field4: i32, //Only generate get_field4()
71/// }
72/// ```
73#[proc_macro_derive(GetSet, attributes(get_set))]
74pub fn get_set(input: TokenStream) -> TokenStream {
75    let input = parse_macro_input!(input as DeriveInput);
76    impl_macro_get_set(&input)
77}
78
79/// 为结构体的每个字段生成对应获取的字段名的方法 (字符串字面量)
80///
81/// 方法名为 `field_{field_name}`, 返回类型为 `&'static str`(如 `field_name`), 值为字段的原始名称(如 `"name"`).
82///
83/// # 示例
84///
85/// ```rust
86/// #[derive(FieldName)]
87/// struct Person {
88///     name: String,
89///     age: u32,
90/// }
91///
92/// impl Person {
93///
94///     pub fn field_name() -> &'static str {
95///         "name"
96///     }
97///
98///     pub fn field_age() -> &'static str {
99///         "age"
100///     }
101/// }
102/// ```
103///
104/// Method for generating corresponding field names for each field of a structure (string literal)
105///
106/// The method name is' field_ {field_name} ', the return type is' static str' (such as' field_name '), and the value is the original name of the field (such as' name')
107///
108/// # Example
109///
110/// ```rust
111/// #[derive(FieldName)]
112/// struct Person {
113///     name: String,
114///     age: u32,
115/// }
116///
117/// impl Person {
118///
119///     pub fn field_name() -> &'static str {
120///         "name"
121///     }
122///
123///     pub fn field_age() -> &'static str {
124///         "age"
125///     }
126/// }
127/// ```
128#[proc_macro_derive(FieldName)]
129pub fn field_name(input: TokenStream) -> TokenStream {
130    let input = parse_macro_input!(input as DeriveInput);
131    impl_macro_field_name(&input)
132}
133
134/// 为结构体实现构建器(Builder)模式
135///
136/// 该宏会生成一个同名的 `Builder` 结构体,并为每个字段提供链式调用的 setter 方法
137/// 最终通过 `build()` 方法构造原始结构体实例
138///
139/// ## 注意
140/// 1. 默认情况下,即使字段为 Option , 在build方法之前使用者也需要对这个字段进行赋值, 除非使用了 default 辅助参数
141///
142/// 支持通过 `#[builder(...)]` 属性对字段行为进行定制<br>
143/// 辅助参数:<br>
144///  `into`: builer 所生成的方法的入参将会是一个实现了 `Into<field>` 的类型<br>
145///  `default`:<br>   1. 在 build 方法中, 如果 builder 结构体中这个字段没有用户的输入, 将会使用 Default::default() 来赋值给原始实例<br>
146///                   2. 如 #\[builder(default = "default_name")\], 将会使用该函数返回值进行赋值
147///
148/// # 示例
149///
150/// ```rust
151/// #[derive(Builder)]
152/// struct User {
153///     #[builder(into)]
154///     name: String,
155///     age: u32,
156/// }
157///
158/// // 生成 UserBuilder 结构体
159///  sturct UserBuilder {
160///     name: Option<String>,
161///     age: Option<u32>,
162/// }
163///
164/// let user = User::builder()
165///     .name("Alice")
166///     .age(30)
167///     .build()
168///     .unwrap();
169/// ```
170///
171/// Implement the Builder pattern for structures
172///
173/// This macro will generate a 'Builder' struct with the same name and provide a chain call setter method for each field
174/// Finally, the 'build()' method is used to construct the original structure instance
175///
176/// ## Attention
177///     1. By default, even if the field is Option, the user still needs to assign a value to this field before the build method, unless the default auxiliary parameter is used
178///
179/// Support customizing field behavior through the '# [builder (...)]' attribute<br>
180/// Auxiliary parameters:<br>
181/// Into: The input parameter of the method generated by the boiler will be a type that implements Into<field><br>
182/// ` default `:<br>1. In the build method, if there is no user input for this field in the builder structure, Default:: default() will be used to assign it to the original instance<br>
183///                   2. If #\[builder (default="default_name")\], the return value of this function will be used for assignment
184///
185/// # Example
186///
187/// ```rust
188/// #[derive(Builder)]
189/// struct User {
190///     #[builder(into)]
191///     name: String,
192///     age: u32,
193/// }
194///
195/// // Generate UserBuilder structure
196///  sturct UserBuilder {
197///     name: Option<String>,
198///     age: Option<u32>,
199/// }
200///
201///
202/// let user = User::builder()
203///     .name("Alice")
204///     .age(30)
205///     .build()
206///     .unwrap();
207/// ```
208#[proc_macro_derive(Builder, attributes(builder))]
209pub fn builder(input: TokenStream) -> TokenStream {
210    let input = parse_macro_input!(input as DeriveInput);
211    impl_macro_builder(&input)
212}
213
214/// 为结构体实现有参构造函数
215///
216/// 该宏会为结构体生成一个默认构造函数,并为每个字段提供对应的参数,
217/// 最终通过参数构造原始结构体实例
218///
219/// ## 注意
220///
221/// 1. 如果字段为 Option 类型, 那么在参数列表中将不会有该字段的入参, 除非使用了 required 辅助参数
222///
223/// 辅助参数:
224///
225/// `required`:  那么即使字段为 Option 也将会出现在方法入参中
226/// `default`:   无此字段入参, 使用 Default::default() 进行赋值
227/// `into`:      入参将会以 impl Into<field> 的类型进行转换
228///
229/// # 示例
230///
231/// ```rust
232/// #[derive(RequiredArgsConstructor)]
233/// struct Person {
234///     #[constructor(into)]
235///     name: String,
236///     age: u32,
237/// }
238///
239/// let person = Person::from_args("Alice", 30);
240/// ```
241///
242/// Implement constructor with required arguments for structures
243///
244/// This macro will generate a default constructor and provide corresponding parameters for each field,
245/// Finally, the parameters are used to construct the original structure instance
246///
247/// ## Attention
248///
249/// 1. If the field is an Option type, it will not appear in the parameter list unless the required auxiliary parameter is used
250///
251/// Auxiliary parameters:
252///
253/// `required`: If the field is an Option type, it will appear in the method parameter list
254/// `default`: If there is no parameter for this field, use Default::default() to assign it
255/// `into`: The parameter will be converted to a type that implements Into<field>
256///
257/// # Example
258///
259/// ```rust
260/// #[derive(RequiredArgsConstructor)]
261/// struct Person {
262///     #[constructor(into)]
263///     name: String,
264///     age: u32,
265/// }
266///
267/// let person = Person::from_args("Alice", 30);
268/// ```
269#[proc_macro_derive(RequiredArgsConstructor, attributes(constructor))]
270pub fn required_args_constructor(input: TokenStream) -> TokenStream {
271    let input = parse_macro_input!(input as DeriveInput);
272    impl_macro_required_args_constructor(&input)
273}
274
275#[proc_macro_derive(Desensitized, attributes(de))]
276pub fn desensitized(input: TokenStream) -> TokenStream {
277    let input = parse_macro_input!(input as DeriveInput);
278    impl_macro_desensitized(&input)
279}
280
281// =============================== Web ===============================
282
283#[doc = ""]
284#[proc_macro_attribute]
285pub fn properties(attr: TokenStream, item: TokenStream) -> TokenStream {
286    let item = parse_macro_input!(item as ItemStruct);
287    impl_macro_properties(attr, item)
288}
289
290#[doc = ""]
291#[proc_macro_attribute]
292pub fn request_mapping(args: TokenStream, input: TokenStream) -> TokenStream {
293    crate::web::routing::with_method(None, args, input)
294}
295
296macro_rules! method_macro {
297    ($method:ident, $variant:ident) => {
298        #[doc = ""]
299        #[proc_macro_attribute]
300        pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
301            crate::web::routing::with_method(
302                Some(crate::web::routing::Method::$variant),
303                args,
304                input,
305            )
306        }
307    };
308}
309
310method_macro!(get_mapping, Get);
311method_macro!(post_mapping, Post);
312method_macro!(put_mapping, Put);
313method_macro!(delete_mapping, Delete);
314method_macro!(patch_mapping, Patch);
315method_macro!(any_mapping, Any);
316
317// #[cfg(feature = "api-doc")]
318#[proc_macro_attribute]
319pub fn api_doc(args: TokenStream, input: TokenStream) -> TokenStream {
320    crate::web::api_doc::impl_macro_api_doc(args, input)
321}
322
323/// A procedural macro attribute for defining scheduled tasks.
324///
325/// This attribute can be applied to a function to register it as a scheduled job
326/// with configurable timing behavior. It supports three scheduling modes:
327///
328/// - **Cron-based scheduling**: via the `cron` parameter (e.g., `"0 0 2 * * *"`).
329/// - **Fixed-rate execution**: via the `fixed_rate` parameter (executes repeatedly at fixed intervals).
330/// - **One-shot execution**: when `one_shot = true`, the task runs once after an optional `initial_delay`.
331///
332/// # Parameters
333///
334/// - `cron`: A cron expression in 6-field format (seconds, minutes, hours, day-of-month, month, day-of-week).
335///   Mutually exclusive with `fixed_rate`.
336/// - `fixed_rate`: Interval between executions (as a positive integer literal).
337///   Mutually exclusive with `cron`.
338/// - `initial_delay`: Delay before the first execution (in units specified by `time_unit`).
339/// - `timezone`: IANA time zone ID (e.g., `"Asia/Shanghai"`, `"UTC"`).
340///   If empty or omitted, the scheduler's default time zone is used.
341/// - `time_unit`: Time unit for `fixed_rate` and `initial_delay` (e.g., `"ms"`, `"s"`, `"m"`).
342///   Interpretation depends on the underlying scheduler.
343/// - `one_shot`: If `true`, the task runs exactly once (typically after `initial_delay`).
344///   In this mode, `cron` and `fixed_rate` are ignored.
345///
346/// # Examples
347///
348/// ```rust
349/// #[Scheduled(cron = "0 0 3 * * *", timezone = "UTC")]
350/// fn daily_cleanup() { /* ... */ }
351///
352/// #[scheduled(fixed_rate = 30, time_unit = "s", initial_delay = 5)]
353/// fn heartbeat() { /* ... */ }
354///
355/// #[Scheduled(one_shot = true, initial_delay = 10, time_unit = "s")]
356/// fn delayed_init() { /* ... */ }
357/// ```
358#[proc_macro_attribute]
359pub fn scheduled(args: TokenStream, input: TokenStream) -> TokenStream {
360    crate::web::scheduled::impl_macro_scheduled(args, input)
361}
362
363/// 实现幂等性属性的过程宏
364///
365/// # 属性参数说明
366/// - `name`:   可选字符串字面量,指定 IdempotencyStore 的单例名称, 可以指定名称使用自己实现的 Store, 默认为`memoryIdempotencyStore`
367/// - `key`:    可选字符串字面量,用于获取请求头的键值,默认值为 `Idempotency-key`
368/// - `cache_key_prefix`: 可选字符串字面量,应用再缓存键的前缀
369/// - `ttl`: 可选整数字面量,缓存存活时间(Time To Live), 默认为秒
370///
371/// # 注意
372/// 当前宏 只支持 Mehod 为 `POST`
373/// 你应该使用 `PostMapping` 或者 `RequestMapping(method = "POST")`
374///
375/// # 示例
376/// ```rust
377/// #[Idempotency(name = "myIdempotencyStore", key = "Idempotency-key", cache_key_prefix = "test", ttl = 6)]
378/// #[PostMapping(path = "/createOrder")]
379/// async fn create_order(order: String) -> impl IntoResponse {
380///
381///     "Ok"
382/// }
383///
384/// ```
385///
386/// Process macros for implementing idempotent properties
387///
388/// # Description of Attribute Parameters
389/// - ` name `: optional string literal, specifying the singleton name of IdempotencyStore, can specify the name to use their own implemented Store, default is ` memoryIdempotencyStore '`
390/// - ` key `: optional string literal, used to obtain the key value of the request header, default value is ` Idempotency key ``
391/// - ` cache_key_prefix `: optional string literal, apply the prefix of the cached key again
392/// - ` ttl `: optional integer face count, cache live time (Time To Live), default is seconds
393///
394/// # Attention
395/// The current macro only supports Mehod as' POST '`
396/// You should use PostMapping or RequestMapping (method="POST")`
397///
398/// # Example
399/// ```rust
400/// #[idempotency(name = "myIdempotencyStore", key = "Idempotency-key", cache_key_prefix = "test", ttl = 6)]
401/// #[PostMapping(path = "/createOrder")]
402/// async fn create_order(order: String) -> impl IntoResponse {
403///
404///     "Ok"
405/// }
406///
407/// ```
408#[proc_macro_attribute]
409pub fn idempotency(attr: TokenStream, item: TokenStream) -> TokenStream {
410    let item_fn = parse_macro_input!(item as ItemFn);
411    impl_macro_idempotency(attr, item_fn)
412}
413
414#[doc = ""]
415#[proc_macro_attribute]
416pub fn pre_authorize(attr: TokenStream, item: TokenStream) -> TokenStream {
417    let item_fn = parse_macro_input!(item as ItemFn);
418    impl_macro_pre_authorize(attr, item_fn)
419}
420
421/// 实现可重试逻辑的过程宏
422///
423/// # 属性参数说明
424/// - `max_attempts`:   最大重试次数,默认为 1
425/// - `delay`:          每次重试的延迟时间,默认为 1000 毫秒
426/// - `backoff`:        可选退避策略路径
427/// - `retry_for`:      需要重试的错误类型列表,匹配这些错误时会触发重试, 这里的类型应该和重试函数的返回类型一致
428/// - `multiplier`:     可选乘数表达式,用于计算每次重试的延迟时间
429///
430/// # 注意
431/// 当前函数应返回 std::result::Result<T, E> 类型,其中 T 为正常返回值类型,E 为需要重试的错误类型
432///
433/// # 示例
434///
435/// ```rust
436/// #[derive(Debug)]
437/// enum TestMatch {
438///    A,
439///    B(u64),
440/// }
441///
442/// #[Retryable(
443///     max_attempts = 3,
444///     delay = 100,
445///     backoff = test_backoff,
446///     retry_for = [TestMatch::A],
447///     multiplier = 2
448/// )]
449/// fn test_retry() -> Result<(), TestMatch> {
450///    let timestamp_sec = std::time::SystemTime::now()
451///       .duration_since(std::time::UNIX_EPOCH)
452///       .unwrap()
453///       .as_secs();
454///     match timestamp_sec % 2 {
455///         0 => Err(TestMatch::B(123)),
456///         _ => Err(TestMatch::A),
457///     }
458/// }
459///
460/// fn test_backoff(error: &TestMatch) {
461///    println!("function test_retry backoff: {:?}", error);
462/// }
463/// ```
464///
465/// Process macros for implementing retry logic
466///
467/// # Description of Attribute Parameters
468/// Max attempts: Maximum number of retries, default is 1
469/// - ` delay `: The default delay time for each retry is 1000 milliseconds
470/// - ` backoff `: Optional backoff policy path
471/// - ` retry_for `: a list of error types that need to be retried. Matching these errors will trigger a retry, and the type here should be consistent with the return type of the retry function
472/// - ` multiplier `: an optional multiplier expression used to calculate the delay time for each retry
473///
474/// # Attention
475/// The current function should return std:: result:: Result<T, E>type, where T is the normal return value type and E is the error type that needs to be retried
476///
477/// # Example
478/// ```rust
479///
480/// #[derive(Debug)]
481/// enum TestMatch {
482///    A,
483///    B(u64),
484/// }
485///
486/// #[Retryable(
487///     max_attempts = 3,
488///     delay = 100,
489///     backoff = test_backoff,
490///     retry_for = [TestMatch::A],
491///     multiplier = 2
492/// )]
493/// fn test_retry() -> Result<(), TestMatch> {
494///    let timestamp_sec = std::time::SystemTime::now()
495///       .duration_since(std::time::UNIX_EPOCH)
496///       .unwrap()
497///       .as_secs();
498///     match timestamp_sec % 2 {
499///         0 => Err(TestMatch::B(123)),
500///         _ => Err(TestMatch::A),
501///     }
502/// }
503///
504/// fn test_backoff(error: &TestMatch) {
505///    println! ("function test_retry backoff: {:?}", error);
506/// }
507/// ```
508// #[cfg(feature = "retry")]
509#[proc_macro_attribute]
510pub fn retryable(attr: TokenStream, item: TokenStream) -> TokenStream {
511    use crate::web::retry::impl_macro_retry;
512
513    let item_fn = parse_macro_input!(item as ItemFn);
514    impl_macro_retry(attr, item_fn)
515}
516
517#[cfg(feature = "translation")]
518#[proc_macro_attribute]
519pub fn translation(attr: TokenStream, item: TokenStream) -> TokenStream {
520    use crate::web::translation::impl_macro_translation;
521
522    let item_fn = parse_macro_input!(item as ItemFn);
523    impl_macro_translation(attr, item_fn)
524}