moduforge-macros-derive 0.7.0

ModuForge-RS 宏扩展模块,提供 Node 和 Mark 的派生宏
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
//! 工具函数模块
//!
//! 提供宏处理过程中常用的工具函数和类型检查功能。
//! 遵循单一职责原则,每个函数都有明确的单一功能。

use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::quote;
use syn::{GenericArgument, PathArguments, Type, TypePath};

/// 生成必要的导入语句
///
/// 为生成的代码添加必要的类型导入,确保生成的代码能够正确编译。
/// 遵循开闭原则,可以通过修改此函数来扩展导入的类型而不影响其他代码。
///
/// # 返回值
///
/// 返回包含所有必要导入语句的 TokenStream
///
/// # 设计原则体现
///
/// - **单一职责**: 只负责生成导入语句
/// - **开闭原则**: 可扩展新的导入而不修改调用方
/// - **接口隔离**: 提供简单明确的导入生成接口
pub fn generate_imports() -> TokenStream2 {
    quote! {
        // 模型类型导入 - Node 和 Mark 生成器所需的导入
        use mf_model::node_definition::NodeSpec;
        use mf_model::schema::AttributeSpec;
        use std::collections::HashMap;
        use serde_json::Value as JsonValue;
    }
}

/// 检查类型是否为 Option<T>
///
/// 分析给定的类型是否为 Option<T> 的形式。
/// 这个函数遵循里氏替换原则,对于任何 Type 都能正确判断。
///
/// # 参数
///
/// * `ty` - 要检查的类型
///
/// # 返回值
///
/// 如果类型是 Option<T> 形式则返回 true,否则返回 false
///
/// # 示例
///
/// ```rust
/// use syn::parse_quote;
/// use crate::common::utils::is_option_type;
///
/// let option_type: syn::Type = parse_quote! { Option<String> };
/// assert!(is_option_type(&option_type));
///
/// let string_type: syn::Type = parse_quote! { String };
/// assert!(!is_option_type(&string_type));
/// ```
///
/// # 设计原则体现
///
/// - **单一职责**: 只负责判断是否为 Option 类型
/// - **里氏替换**: 任何 Type 实现都能正确处理
pub fn is_option_type(ty: &Type) -> bool {
    match ty {
        Type::Path(TypePath { path, .. }) => {
            // 检查路径的最后一个段是否为 "Option"
            if let Some(segment) = path.segments.last() {
                segment.ident == "Option"
            } else {
                false
            }
        },
        _ => false,
    }
}

/// 提取 Option<T> 中的内部类型 T
///
/// 从 Option<T> 类型中提取内部的类型 T。
/// 如果输入不是 Option<T> 类型,则返回 None。
///
/// # 参数
///
/// * `ty` - Option<T> 类型
///
/// # 返回值
///
/// 如果成功提取则返回 Some(&T),否则返回 None
///
/// # 示例
///
/// ```rust
/// use syn::parse_quote;
/// use crate::common::utils::extract_option_inner_type;
///
/// let option_type: syn::Type = parse_quote! { Option<String> };
/// let inner = extract_option_inner_type(&option_type);
/// assert!(inner.is_some());
/// ```
///
/// # 设计原则体现
///
/// - **单一职责**: 只负责提取 Option 的内部类型
/// - **接口隔离**: 提供明确的类型提取接口
pub fn extract_option_inner_type(ty: &Type) -> Option<&Type> {
    match ty {
        Type::Path(TypePath { path, .. }) => {
            // 获取路径的最后一个段
            let last_segment = path.segments.last()?;

            // 确认是 Option 类型
            if last_segment.ident != "Option" {
                return None;
            }

            // 提取泛型参数
            match &last_segment.arguments {
                PathArguments::AngleBracketed(args) => {
                    // 获取第一个泛型参数
                    args.args.first().and_then(|arg| match arg {
                        GenericArgument::Type(ty) => Some(ty),
                        _ => None,
                    })
                },
                _ => None,
            }
        },
        _ => None,
    }
}

/// 生成字段到 JsonValue 的转换代码
///
/// 根据字段类型生成相应的转换代码,将字段值转换为 serde_json::Value。
/// 此函数体现了开闭原则,可以通过添加新的类型处理分支来扩展功能。
///
/// # 参数
///
/// * `field_name` - 字段名称
/// * `field_type` - 字段类型
///
/// # 返回值
///
/// 返回转换代码的 TokenStream
///
/// # 转换规则
///
/// - Option<T> 类型: 如果是 Some(value) 则转换 value,如果是 None 则返回 JsonValue::Null
/// - 普通类型: 直接使用 serde_json::to_value 转换
///
/// # 示例生成的代码
///
/// ```rust
/// // 对于 Option<String> 类型的字段 name
/// self.name.as_ref().map(|v| serde_json::to_value(v).unwrap_or(JsonValue::Null))
///     .unwrap_or(JsonValue::Null)
///
/// // 对于 String 类型的字段 title  
/// serde_json::to_value(&self.title).unwrap_or(JsonValue::Null)
/// ```
///
/// # 设计原则体现
///
/// - **开闭原则**: 可扩展新的类型转换而不修改现有逻辑
/// - **单一职责**: 只负责生成字段转换代码
pub fn generate_field_conversion(
    field_name: &Ident,
    field_type: &Type,
) -> TokenStream2 {
    if is_option_type(field_type) {
        // Option<T> 类型的转换逻辑
        quote! {
            self.#field_name.as_ref()
                .map(|v| serde_json::to_value(v).unwrap_or(JsonValue::Null))
                .unwrap_or(JsonValue::Null)
        }
    } else {
        // 普通类型的转换逻辑
        quote! {
            serde_json::to_value(&self.#field_name).unwrap_or(JsonValue::Null)
        }
    }
}

/// 检查类型是否为支持的基本类型
///
/// 验证字段类型是否为宏系统支持的基本类型。
/// 遵循开闭原则,通过修改支持类型列表来扩展功能。
///
/// # 参数
///
/// * `ty` - 要检查的类型
///
/// # 返回值
///
/// 如果类型受支持则返回 true,否则返回 false
///
/// # 支持的类型
///
/// - String, str (字符串类型)
/// - i32, i64, u32, u64 (整数类型)
/// - f32, f64 (浮点数类型)
/// - bool (布尔类型)
/// - usize, isize (指针大小整数类型)
///
/// # 设计原则体现
///
/// - **开闭原则**: 通过修改类型列表扩展而不修改核心逻辑
/// - **单一职责**: 只负责类型支持性检查
pub fn is_supported_basic_type(ty: &Type) -> bool {
    // 支持的基本类型列表
    const SUPPORTED_TYPES: &[&str] = &[
        "String",
        "str",
        "&str",
        "i32",
        "i64",
        "u32",
        "u64",
        "i8",
        "i16",
        "u8",
        "u16",
        "i128",
        "u128",
        "f32",
        "f64",
        "bool",
        "usize",
        "isize",
        "serde_json::Value",
        "Value",
        "uuid::Uuid",
        "Uuid",
        "Vec<u8>",
        "Vec<String>",
    ];

    // 获取类型的字符串表示并去除空格
    let type_str = quote! { #ty }.to_string().replace(" ", "");

    // 检查是否精确匹配支持的类型(而不是简单的包含)
    SUPPORTED_TYPES.iter().any(|&supported| type_str == supported)
}

/// 检查类型是否为支持的类型(包括 Option 包装)
///
/// 检查类型是否为直接支持的基本类型或其 Option 包装版本。
/// 此函数遵循里氏替换原则,可以处理所有合法的类型输入。
///
/// # 参数
///
/// * `ty` - 要检查的类型
///
/// # 返回值
///
/// 如果类型受支持则返回 true,否则返回 false
///
/// # 示例
///
/// ```rust
/// use syn::parse_quote;
/// use crate::common::utils::is_supported_type;
///
/// let string_type: syn::Type = parse_quote! { String };
/// assert!(is_supported_type(&string_type));
///
/// let option_string: syn::Type = parse_quote! { Option<String> };
/// assert!(is_supported_type(&option_string));
///
/// let unsupported: syn::Type = parse_quote! { Vec<String> };
/// assert!(!is_supported_type(&unsupported));
/// ```
///
/// # 设计原则体现
///
/// - **里氏替换**: 任何 Type 都能正确处理
/// - **单一职责**: 专门负责检查类型支持性
pub fn is_supported_type(ty: &Type) -> bool {
    if is_option_type(ty) {
        // 对于 Option<T>,检查内部类型 T 是否支持
        if let Some(inner_type) = extract_option_inner_type(ty) {
            is_supported_basic_type(inner_type)
        } else {
            false
        }
    } else {
        // 对于普通类型,直接检查是否支持
        is_supported_basic_type(ty)
    }
}

/// 提取类型的简单名称
///
/// 从复杂的类型路径中提取简单的类型名称,用于错误消息和调试。
/// 遵循接口隔离原则,提供简洁明确的类型名称提取功能。
///
/// # 参数
///
/// * `ty` - 要提取名称的类型
///
/// # 返回值
///
/// 返回类型的简单名称字符串
///
/// # 示例
///
/// ```rust
/// use syn::parse_quote;
/// use crate::common::utils::extract_type_name;
///
/// let ty: syn::Type = parse_quote! { std::option::Option<String> };
/// assert_eq!(extract_type_name(&ty), "Option<String>");
/// ```
///
/// # 设计原则体现
///
/// - **接口隔离**: 只提供类型名称提取功能
/// - **单一职责**: 专门负责类型名称的提取和格式化
pub fn extract_type_name(ty: &Type) -> String {
    match ty {
        Type::Path(type_path) => {
            // 提取路径的各个段
            let segments: Vec<String> = type_path
                .path
                .segments
                .iter()
                .map(|segment| {
                    let ident = &segment.ident;
                    match &segment.arguments {
                        PathArguments::AngleBracketed(args) => {
                            // 处理泛型参数
                            let args_str: Vec<String> = args
                                .args
                                .iter()
                                .map(|arg| match arg {
                                    GenericArgument::Type(ty) => {
                                        extract_type_name(ty)
                                    },
                                    _ => "?".to_string(),
                                })
                                .collect();
                            format!("{}<{}>", ident, args_str.join(", "))
                        },
                        _ => ident.to_string(),
                    }
                })
                .collect();

            // 返回最后一个段作为类型名称
            segments.last().cloned().unwrap_or_else(|| "Unknown".to_string())
        },
        _ => {
            // 对于其他类型,返回其 TokenStream 表示
            quote! { #ty }.to_string()
        },
    }
}

/// 生成属性设置代码
///
/// 为给定的字段生成设置属性的代码,调用相应的 set_attr 方法。
/// 此函数遵循单一职责原则,专门负责属性设置代码的生成。
///
/// # 参数
///
/// * `field_name` - 字段名称标识符
/// * `field_type` - 字段类型
/// * `target` - 目标对象名称 (如 "node" 或 "mark")
///
/// # 返回值
///
/// 返回设置属性的代码 TokenStream
///
/// # 生成的代码示例
///
/// ```rust
/// // 对于字段名 "name",类型 String
/// node.set_attr("name", Some(serde_json::to_value(&self.name).unwrap_or(JsonValue::Null)));
/// ```
///
/// # 设计原则体现
///
/// - **单一职责**: 只负责生成属性设置代码
/// - **开闭原则**: 可扩展不同的目标类型而不修改核心逻辑
pub fn generate_attr_setter_code(
    field_name: &Ident,
    field_type: &Type,
    target: &str,
) -> TokenStream2 {
    let conversion = generate_field_conversion(field_name, field_type);
    let field_name_str = field_name.to_string();
    let target_ident = syn::parse_str::<Ident>(target)
        .unwrap_or_else(|_| syn::parse_str("target").unwrap());

    quote! {
        #target_ident.set_attr(#field_name_str, Some(#conversion));
    }
}

/// 验证标识符格式
///
/// 验证字符串是否为有效的 Rust 标识符格式。
/// 遵循单一职责原则,专门负责标识符格式验证。
///
/// # 参数
///
/// * `identifier` - 要验证的标识符字符串
///
/// # 返回值
///
/// 如果标识符格式有效则返回 true,否则返回 false
///
/// # 验证规则
///
/// - 不能为空字符串
/// - 只能包含字母、数字和下划线
/// - 必须以字母或下划线开头
///
/// # 设计原则体现
///
/// - **单一职责**: 只负责标识符格式验证
/// - **接口隔离**: 提供简单的验证接口
pub fn is_valid_identifier(identifier: &str) -> bool {
    if identifier.is_empty() {
        return false;
    }

    // 检查第一个字符是否为字母或下划线
    let first_char = identifier.chars().next().unwrap();
    if !first_char.is_alphabetic() && first_char != '_' {
        return false;
    }

    // 检查其余字符是否为字母、数字或下划线
    identifier.chars().all(|c| c.is_alphanumeric() || c == '_')
}

#[cfg(test)]
mod tests {
    use super::*;
    use syn::parse_quote;

    /// 测试 Option 类型检查功能
    #[test]
    fn test_is_option_type() {
        let option_string: Type = parse_quote! { Option<String> };
        assert!(is_option_type(&option_string));

        let string: Type = parse_quote! { String };
        assert!(!is_option_type(&string));

        let option_int: Type = parse_quote! { Option<i32> };
        assert!(is_option_type(&option_int));
    }

    /// 测试提取 Option 内部类型功能
    #[test]
    fn test_extract_option_inner_type() {
        let option_string: Type = parse_quote! { Option<String> };
        let inner = extract_option_inner_type(&option_string);
        assert!(inner.is_some());

        let string: Type = parse_quote! { String };
        let inner = extract_option_inner_type(&string);
        assert!(inner.is_none());
    }

    /// 测试支持类型检查功能
    #[test]
    fn test_is_supported_type() {
        let string: Type = parse_quote! { String };
        assert!(is_supported_type(&string));

        let option_string: Type = parse_quote! { Option<String> };
        assert!(is_supported_type(&option_string));

        let vec_string: Type = parse_quote! { Vec<String> };
        assert!(!is_supported_type(&vec_string));

        let i32_type: Type = parse_quote! { i32 };
        assert!(is_supported_type(&i32_type));

        let option_i32: Type = parse_quote! { Option<i32> };
        assert!(is_supported_type(&option_i32));
    }

    /// 测试类型名称提取功能
    #[test]
    fn test_extract_type_name() {
        let string: Type = parse_quote! { String };
        assert_eq!(extract_type_name(&string), "String");

        let option_string: Type = parse_quote! { Option<String> };
        assert_eq!(extract_type_name(&option_string), "Option<String>");

        let option_i32: Type = parse_quote! { Option<i32> };
        assert_eq!(extract_type_name(&option_i32), "Option<i32>");
    }

    /// 测试标识符格式验证功能
    #[test]
    fn test_is_valid_identifier() {
        assert!(is_valid_identifier("valid_name"));
        assert!(is_valid_identifier("ValidName"));
        assert!(is_valid_identifier("valid123"));
        assert!(is_valid_identifier("_private"));

        assert!(!is_valid_identifier(""));
        assert!(!is_valid_identifier("123invalid"));
        assert!(!is_valid_identifier("invalid-name"));
        assert!(!is_valid_identifier("invalid name"));
    }

    /// 测试导入语句生成功能
    #[test]
    fn test_generate_imports() {
        let imports = generate_imports();
        let imports_str = imports.to_string();

        assert!(imports_str.contains("mf_model :: node_type :: NodeSpec"));
        assert!(imports_str.contains("mf_model :: schema :: AttributeSpec"));
        assert!(imports_str.contains("serde_json :: Value"));
        assert!(imports_str.contains("std :: collections :: HashMap"));
    }

    /// 测试字段转换代码生成功能
    #[test]
    fn test_generate_field_conversion() {
        let field_name = syn::parse_str::<Ident>("test_field").unwrap();

        // 测试普通类型转换
        let string_type: Type = parse_quote! { String };
        let conversion = generate_field_conversion(&field_name, &string_type);
        let conversion_str = conversion.to_string();
        assert!(conversion_str.contains("serde_json :: to_value"));
        assert!(conversion_str.contains("test_field"));

        // 测试 Option 类型转换
        let option_type: Type = parse_quote! { Option<String> };
        let conversion = generate_field_conversion(&field_name, &option_type);
        let conversion_str = conversion.to_string();
        assert!(conversion_str.contains("as_ref"));
        assert!(conversion_str.contains("unwrap_or"));
    }
}