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}