imitatort 0.0.1-SNAPSHOT-dev.20260302111239

轻量级多Agent公司模拟框架
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
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
//! ToolProvider 实现
//!
//! 提供框架工具和应用工具的解耦查询机制

use crate::core::tool::{CategoryNode, ToolRegistry};
use crate::domain::tool::{CategoryNodeInfo, MatchType, Tool, ToolProvider};
use std::sync::Arc;

/// 组合 ToolProvider
///
/// 同时查询多个 Provider,合并结果
pub struct CompositeToolProvider {
    providers: Vec<Box<dyn ToolProvider>>,
}

impl CompositeToolProvider {
    /// 创建空的组合提供者
    pub fn new() -> Self {
        Self {
            providers: Vec::new(),
        }
    }

    /// 添加提供者
    pub fn add_provider(mut self, provider: Box<dyn ToolProvider>) -> Self {
        self.providers.push(provider);
        self
    }

    /// 添加应用工具提供者(从 ToolRegistry 创建)
    pub fn with_registry(self, registry: Arc<ToolRegistry>) -> Self {
        self.add_provider(Box::new(RegistryToolProvider::new(registry)))
    }
}

impl Default for CompositeToolProvider {
    fn default() -> Self {
        Self::new()
    }
}

impl ToolProvider for CompositeToolProvider {
    fn list_tools(&self) -> Vec<Tool> {
        let mut tools = Vec::new();
        for provider in &self.providers {
            tools.extend(provider.list_tools());
        }
        tools
    }

    fn search_tools(&self, query: &str, match_type: MatchType) -> Vec<Tool> {
        let mut tools = Vec::new();
        for provider in &self.providers {
            tools.extend(provider.search_tools(query, match_type));
        }
        tools
    }

    fn list_tools_by_category(&self, category: &str) -> Vec<Tool> {
        let mut tools = Vec::new();
        for provider in &self.providers {
            tools.extend(provider.list_tools_by_category(category));
        }
        tools
    }

    fn get_category_tree(&self) -> CategoryNodeInfo {
        let mut root = CategoryNodeInfo::new("root", "");

        // 收集所有提供者的分类
        for provider in &self.providers {
            let provider_tree = provider.get_category_tree();
            merge_category_tree(&mut root, provider_tree);
        }

        root
    }
}

/// 从 ToolRegistry 创建的提供者
pub struct RegistryToolProvider {
    registry: Arc<ToolRegistry>,
}

impl RegistryToolProvider {
    /// 从 ToolRegistry 创建提供者
    pub fn new(registry: Arc<ToolRegistry>) -> Self {
        Self { registry }
    }
}

impl ToolProvider for RegistryToolProvider {
    fn list_tools(&self) -> Vec<Tool> {
        self.registry.list_all()
    }

    fn search_tools(&self, query: &str, match_type: MatchType) -> Vec<Tool> {
        let all_tools = self.registry.list_all();
        let query_lower = query.to_lowercase();

        all_tools
            .into_iter()
            .filter(|tool| {
                match match_type {
                    MatchType::Exact => {
                        // 精确匹配:ID、名称或完整分类路径
                        tool.id.to_lowercase() == query_lower
                            || tool.name.to_lowercase() == query_lower
                            || tool.category.to_path_string().to_lowercase() == query_lower
                    }
                    MatchType::Fuzzy => {
                        // 模糊匹配:ID、名称、描述、分类包含查询词
                        tool.id.to_lowercase().contains(&query_lower)
                            || tool.name.to_lowercase().contains(&query_lower)
                            || tool.description.to_lowercase().contains(&query_lower)
                            || tool
                                .category
                                .to_path_string()
                                .to_lowercase()
                                .contains(&query_lower)
                    }
                }
            })
            .collect()
    }

    fn list_tools_by_category(&self, category: &str) -> Vec<Tool> {
        // 在同步 trait 方法中执行异步代码
        // 注意:这需要在 tokio 运行时上下文中调用
        let rt = tokio::runtime::Handle::try_current();
        match rt {
            Ok(handle) => tokio::task::block_in_place(|| {
                handle.block_on(async { self.registry.find_by_category(category).await })
            }),
            Err(_) => {
                // 如果不在运行时中,返回空列表
                Vec::new()
            }
        }
    }

    fn get_category_tree(&self) -> CategoryNodeInfo {
        let rt = tokio::runtime::Handle::try_current();
        match rt {
            Ok(handle) => tokio::task::block_in_place(|| {
                handle.block_on(async {
                    let root = self.registry.get_category_tree().await;
                    convert_to_info(&root, "")
                })
            }),
            Err(_) => CategoryNodeInfo::new("root", ""),
        }
    }
}

/// 将 CategoryNode 转换为 CategoryNodeInfo
fn convert_to_info(node: &CategoryNode, parent_path: &str) -> CategoryNodeInfo {
    let path = if parent_path.is_empty() {
        node.name.clone()
    } else {
        format!("{}/{}", parent_path, node.name)
    };

    let mut info = CategoryNodeInfo::new(&node.name, &path);
    info.tool_count = node.tools().len();

    for child in node.children().values() {
        info.children.push(convert_to_info(child, &path));
    }

    info
}

/// 合并两个分类树
fn merge_category_tree(target: &mut CategoryNodeInfo, source: CategoryNodeInfo) {
    target.tool_count += source.tool_count;

    for source_child in source.children {
        let mut found = false;
        for target_child in &mut target.children {
            if target_child.name == source_child.name {
                merge_category_tree(target_child, source_child.clone());
                found = true;
                break;
            }
        }

        if !found {
            target.children.push(source_child);
        }
    }
}

/// 框架内置工具提供者
///
/// 提供框架级别的工具定义(不执行,只提供元数据)
pub struct FrameworkToolProvider;

impl FrameworkToolProvider {
    /// 创建框架工具提供者
    pub fn new() -> Self {
        Self
    }

    /// 获取所有框架工具定义
    pub fn get_framework_tools() -> Vec<Tool> {
        vec![
            // Tool 查询类
            Self::create_tool_search(),
            Self::create_tool_list_categories(),
            Self::create_tool_get_category_tools(),
            // 消息发送类
            Self::create_message_send_direct(),
            Self::create_message_send_group(),
            Self::create_message_reply(),
            // 时间类
            Self::create_time_now(),
            // 组织架构类
            Self::create_org_get_structure(),
            Self::create_org_get_department(),
            Self::create_org_get_leader(),
            Self::create_org_find_agents(),
            Self::create_org_get_sub_departments(),
            Self::create_org_get_subordinates(),
            // 文件操作类
            Self::create_file_read(),
            Self::create_file_write(),
            Self::create_file_delete(),
            Self::create_file_list(),
            // 命令执行类
            Self::create_shell_exec(),
            // 网页请求类
            Self::create_http_fetch(),
        ]
    }

    fn create_tool_search() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "tool.search",
            "搜索工具",
            "按名称、描述、分类搜索工具,支持精确匹配和模糊匹配",
            CategoryPath::from_string("tool/query"),
            JsonSchema::object()
                .property("query", JsonSchema::string().description("搜索关键词"))
                .property(
                    "match_type",
                    JsonSchema::string()
                        .description("匹配类型:exact(精确) 或 fuzzy(模糊)")
                        .optional(),
                )
                .property(
                    "category_filter",
                    JsonSchema::string()
                        .description("限制在指定分类下搜索")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "匹配的工具列表",
            json!({"type": "array", "items": {"type": "object"}}),
        ))
    }

    fn create_tool_list_categories() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "tool.list_categories",
            "列出工具分类",
            "获取所有工具的分类层级结构",
            CategoryPath::from_string("tool/query"),
            JsonSchema::object()
                .property(
                    "parent_category",
                    JsonSchema::string()
                        .description("父分类路径,为空则返回所有根分类")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "分类树列表",
            json!({"type": "array", "items": {"type": "object"}}),
        ))
    }

    fn create_tool_get_category_tools() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "tool.get_category_tools",
            "获取分类工具",
            "获取指定分类下的所有工具",
            CategoryPath::from_string("tool/query"),
            JsonSchema::object()
                .property(
                    "category",
                    JsonSchema::string().description("分类路径,如 'tool/query'"),
                )
                .property(
                    "recursive",
                    JsonSchema::boolean()
                        .description("是否包含子分类的工具")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "工具列表",
            json!({"type": "array", "items": {"type": "object"}}),
        ))
    }

    fn create_message_send_direct() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "message.send_direct",
            "发送私聊消息",
            "向指定 Agent 发送私聊消息",
            CategoryPath::from_string("message/send"),
            JsonSchema::object()
                .property(
                    "to_agent_id",
                    JsonSchema::string().description("接收者 Agent ID"),
                )
                .property("content", JsonSchema::string().description("消息内容"))
                .property(
                    "reply_to_message_id",
                    JsonSchema::string().description("回复的消息ID").optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new("发送结果", json!({"type": "boolean"})))
    }

    fn create_message_send_group() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "message.send_group",
            "发送群聊消息",
            "向指定群组发送消息,支持 @ 他人",
            CategoryPath::from_string("message/send"),
            JsonSchema::object()
                .property("group_id", JsonSchema::string().description("群组 ID"))
                .property("content", JsonSchema::string().description("消息内容"))
                .property(
                    "mention_agent_ids",
                    JsonSchema::string_array()
                        .description("需要 @ 的 Agent ID 列表")
                        .optional(),
                )
                .property(
                    "reply_to_message_id",
                    JsonSchema::string().description("回复的消息ID").optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new("发送结果", json!({"type": "boolean"})))
    }

    fn create_message_reply() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "message.reply",
            "回复消息",
            "引用并回复指定消息,可同时 @ 他人",
            CategoryPath::from_string("message/send"),
            JsonSchema::object()
                .property(
                    "message_id",
                    JsonSchema::string().description("要回复的消息ID"),
                )
                .property("content", JsonSchema::string().description("回复内容"))
                .property(
                    "mention_agent_ids",
                    JsonSchema::string_array()
                        .description("需要 @ 的 Agent ID 列表")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new("发送结果", json!({"type": "boolean"})))
    }

    fn create_time_now() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "time.now",
            "获取当前时间",
            "获取系统当前时间",
            CategoryPath::from_string("time/query"),
            JsonSchema::object().build(),
        )
        .with_returns(ReturnType::new("当前时间信息", json!({"type": "object"})))
    }

    fn create_org_get_structure() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "org.get_structure",
            "获取组织架构",
            "获取完整的组织架构树",
            CategoryPath::from_string("org/query"),
            JsonSchema::object().build(),
        )
        .with_returns(ReturnType::new("组织架构树", json!({"type": "object"})))
    }

    fn create_org_get_department() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "org.get_department",
            "Get Department Info",
            "Get detailed information of specified department",
            CategoryPath::from_string("org/query"),
            JsonSchema::object()
                .property(
                    "department_id",
                    JsonSchema::string().description("Department ID"),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "Department Info",
            json!({"type": "object"}),
        ))
    }

    fn create_org_get_leader() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "org.get_leader",
            "Get Department Leader",
            "Get the leader information of specified department",
            CategoryPath::from_string("org/query"),
            JsonSchema::object()
                .property(
                    "department_id",
                    JsonSchema::string().description("Department ID"),
                )
                .build(),
        )
        .with_returns(ReturnType::new("领导信息", json!({"type": "object"})))
    }

    fn create_org_find_agents() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "org.find_agents",
            "查找 Agent",
            "Multi-dimensional Agent search by ID, name, position, department, description, etc.",
            CategoryPath::from_string("org/query"),
            JsonSchema::object()
                .property(
                    "query_type",
                    JsonSchema::string()
                        .description("查询类型:id, name, role, department, description"),
                )
                .property("query_value", JsonSchema::string().description("查询值"))
                .property(
                    "fuzzy_match",
                    JsonSchema::boolean().description("是否模糊匹配").optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "匹配的 Agent 列表",
            json!({"type": "array", "items": {"type": "object"}}),
        ))
    }

    fn create_org_get_sub_departments() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "org.get_sub_departments",
            "Get Sub-departments",
            "Get the list of direct sub-departments of specified department",
            CategoryPath::from_string("org/query"),
            JsonSchema::object()
                .property(
                    "department_id",
                    JsonSchema::string().description("Department ID"),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "Sub-department List",
            json!({"type": "array", "items": {"type": "object"}}),
        ))
    }

    fn create_org_get_subordinates() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "org.get_subordinates",
            "Get Subordinates",
            "Get the list of subordinates for specified Agent (based on department leadership relationship)",
            CategoryPath::from_string("org/query"),
            JsonSchema::object()
                .property("agent_id", JsonSchema::string().description("Agent ID"))
                .build(),
        )
        .with_returns(ReturnType::new(
            "Subordinate Agent List",
            json!({"type": "array", "items": {"type": "object"}}),
        ))
    }

    // ==================== 文件操作类 ====================

    fn create_file_read() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "file.read",
            "读取文件",
            "读取文件的全部内容",
            CategoryPath::from_string("file/read"),
            JsonSchema::object()
                .property("path", JsonSchema::string().description("文件路径"))
                .build(),
        )
        .with_returns(ReturnType::new(
            "文件内容",
            json!({"type": "object", "properties": {"success": {"type": "boolean"}, "content": {"type": "string"}, "error": {"type": "string"}}}),
        ))
    }

    fn create_file_write() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "file.write",
            "写入文件",
            "写入内容到文件,支持覆盖或追加模式",
            CategoryPath::from_string("file/write"),
            JsonSchema::object()
                .property("path", JsonSchema::string().description("文件路径"))
                .property("content", JsonSchema::string().description("要写入的内容"))
                .property(
                    "append",
                    JsonSchema::boolean()
                        .description("是否追加模式(默认 false 覆盖写入)")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "写入结果",
            json!({"type": "object", "properties": {"success": {"type": "boolean"}, "error": {"type": "string"}}}),
        ))
    }

    fn create_file_delete() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "file.delete",
            "删除文件",
            "删除指定文件",
            CategoryPath::from_string("file/delete"),
            JsonSchema::object()
                .property("path", JsonSchema::string().description("文件路径"))
                .build(),
        )
        .with_returns(ReturnType::new(
            "删除结果",
            json!({"type": "object", "properties": {"success": {"type": "boolean"}, "error": {"type": "string"}}}),
        ))
    }

    fn create_file_list() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "file.list",
            "列出目录",
            "列出目录下的文件和子目录,支持通配符过滤",
            CategoryPath::from_string("file/list"),
            JsonSchema::object()
                .property("path", JsonSchema::string().description("目录路径"))
                .property(
                    "pattern",
                    JsonSchema::string()
                        .description("通配符过滤模式(如 *.rs)")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "目录列表",
            json!({"type": "object", "properties": {"success": {"type": "boolean"}, "entries": {"type": "array", "items": {"type": "object"}}, "error": {"type": "string"}}}),
        ))
    }

    // ==================== 命令执行类 ====================

    fn create_shell_exec() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "shell.exec",
            "执行 Shell 命令",
            "执行系统 Shell 命令并返回输出",
            CategoryPath::from_string("shell/execute"),
            JsonSchema::object()
                .property("command", JsonSchema::string().description("要执行的命令"))
                .property(
                    "timeout",
                    JsonSchema::integer()
                        .description("超时时间(秒),默认 60 秒")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "执行结果",
            json!({"type": "object", "properties": {"success": {"type": "boolean"}, "stdout": {"type": "string"}, "stderr": {"type": "string"}, "exit_code": {"type": "integer"}, "error": {"type": "string"}}}),
        ))
    }

    // ==================== 网页请求类 ====================

    fn create_http_fetch() -> Tool {
        use crate::domain::tool::{CategoryPath, JsonSchema, ReturnType};
        use serde_json::json;

        Tool::new(
            "http.fetch",
            "获取网页内容",
            "使用 Chrome 浏览器特征请求头获取网页内容",
            CategoryPath::from_string("http/fetch"),
            JsonSchema::object()
                .property("url", JsonSchema::string().description("要获取的 URL"))
                .property(
                    "method",
                    JsonSchema::string()
                        .description("HTTP 方法(GET/POST 等,默认 GET)")
                        .optional(),
                )
                .property(
                    "timeout",
                    JsonSchema::integer()
                        .description("超时时间(秒),默认 30 秒")
                        .optional(),
                )
                .build(),
        )
        .with_returns(ReturnType::new(
            "响应内容",
            json!({"type": "object", "properties": {"success": {"type": "boolean"}, "status": {"type": "integer"}, "body": {"type": "string"}, "headers": {"type": "object"}, "error": {"type": "string"}}}),
        ))
    }
}

impl Default for FrameworkToolProvider {
    fn default() -> Self {
        Self::new()
    }
}

impl ToolProvider for FrameworkToolProvider {
    fn list_tools(&self) -> Vec<Tool> {
        Self::get_framework_tools()
    }

    fn search_tools(&self, query: &str, match_type: MatchType) -> Vec<Tool> {
        let all_tools = Self::get_framework_tools();
        let query_lower = query.to_lowercase();

        all_tools
            .into_iter()
            .filter(|tool| match match_type {
                MatchType::Exact => {
                    tool.id.to_lowercase() == query_lower || tool.name.to_lowercase() == query_lower
                }
                MatchType::Fuzzy => {
                    tool.id.to_lowercase().contains(&query_lower)
                        || tool.name.to_lowercase().contains(&query_lower)
                        || tool.description.to_lowercase().contains(&query_lower)
                }
            })
            .collect()
    }

    fn list_tools_by_category(&self, category: &str) -> Vec<Tool> {
        let all_tools = Self::get_framework_tools();
        let category_path = crate::domain::tool::CategoryPath::from_string(category);

        all_tools
            .into_iter()
            .filter(|tool| tool.category == category_path || tool.category.contains(&category_path))
            .collect()
    }

    fn get_category_tree(&self) -> CategoryNodeInfo {
        let mut root = CategoryNodeInfo::new("root", "");

        for tool in Self::get_framework_tools() {
            add_tool_to_tree(&mut root, &tool);
        }

        root
    }
}

/// 将工具添加到分类树
fn add_tool_to_tree(root: &mut CategoryNodeInfo, tool: &Tool) {
    let segments = tool.category.segments();

    let mut current = root;
    let mut current_path = String::new();

    for segment in segments.iter() {
        if !current_path.is_empty() {
            current_path.push('/');
        }
        current_path.push_str(segment);

        // 查找或创建子节点
        let child_index = current.children.iter().position(|c| c.name == *segment);

        match child_index {
            Some(index) => {
                current = &mut current.children[index];
            }
            None => {
                let new_child = CategoryNodeInfo::new(segment, &current_path);
                current.children.push(new_child);
                let index = current.children.len() - 1;
                current = &mut current.children[index];
            }
        }
    }

    current.tool_count += 1;
}