Skip to main content

aster/map/server/services/
architecture.rs

1//! 架构分析服务
2//!
3//! 负责构建逻辑架构图、模块详情、符号引用等
4
5use regex::Regex;
6use std::collections::{HashMap, HashSet};
7
8use crate::map::server::types::{
9    ArchitectureMap, CallerInfo, LineLocation, LogicBlock, LogicBlockType, ModuleDetailInfo,
10    ModuleSymbols, SymbolInfo, SymbolLocation, SymbolRefInfo, TypeRefInfo,
11};
12use crate::map::types_enhanced::{EnhancedCodeBlueprint, EnhancedModule};
13
14/// 获取模块所在目录
15pub fn get_dir(module_id: &str) -> String {
16    let parts: Vec<&str> = module_id.split('/').collect();
17    match parts.len() {
18        1 => ".".to_string(),
19        2 => parts[0].to_string(),
20        _ => parts[..parts.len() - 1].join("/"),
21    }
22}
23
24/// 目录类型模式
25struct TypePattern {
26    pattern: Regex,
27    block_type: LogicBlockType,
28    name: &'static str,
29}
30
31impl TypePattern {
32    fn new(pattern: &str, block_type: LogicBlockType, name: &'static str) -> Self {
33        Self {
34            pattern: Regex::new(pattern).unwrap(),
35            block_type,
36            name,
37        }
38    }
39}
40
41/// 构建逻辑架构图
42pub fn build_architecture_map(blueprint: &EnhancedCodeBlueprint) -> ArchitectureMap {
43    let modules: Vec<&EnhancedModule> = blueprint.modules.values().collect();
44
45    // 按目录分组
46    let mut dir_groups: HashMap<String, Vec<&EnhancedModule>> = HashMap::new();
47    for module in &modules {
48        let dir = get_dir(&module.id);
49        dir_groups.entry(dir).or_default().push(module);
50    }
51
52    // 类型模式定义
53    let type_patterns = vec![
54        TypePattern::new(r"^(src/)?cli", LogicBlockType::Entry, "程序入口"),
55        TypePattern::new(r"^(src/)?core", LogicBlockType::Core, "核心引擎"),
56        TypePattern::new(r"^(src/)?tools?", LogicBlockType::Feature, "工具系统"),
57        TypePattern::new(r"^(src/)?commands?", LogicBlockType::Feature, "命令处理"),
58        TypePattern::new(r"^(src/)?ui", LogicBlockType::Ui, "用户界面"),
59        TypePattern::new(r"^(src/)?hooks?", LogicBlockType::Feature, "钩子系统"),
60        TypePattern::new(r"^(src/)?plugins?", LogicBlockType::Feature, "插件系统"),
61        TypePattern::new(r"^(src/)?config", LogicBlockType::Config, "配置管理"),
62        TypePattern::new(r"^(src/)?session", LogicBlockType::Data, "会话管理"),
63        TypePattern::new(r"^(src/)?context", LogicBlockType::Core, "上下文管理"),
64        TypePattern::new(r"^(src/)?streaming", LogicBlockType::Core, "流式处理"),
65        TypePattern::new(r"^(src/)?providers?", LogicBlockType::Core, "API 提供者"),
66        TypePattern::new(r"^(src/)?utils?", LogicBlockType::Util, "工具函数"),
67        TypePattern::new(r"^(src/)?parser", LogicBlockType::Util, "代码解析"),
68        TypePattern::new(r"^(src/)?search", LogicBlockType::Util, "代码搜索"),
69        TypePattern::new(r"^(src/)?map", LogicBlockType::Feature, "代码地图"),
70        TypePattern::new(r"^(src/)?mcp", LogicBlockType::Feature, "MCP 服务"),
71        TypePattern::new(r"^(src/)?ide", LogicBlockType::Feature, "IDE 集成"),
72    ];
73
74    // 为每个目录创建逻辑块
75    let mut blocks: Vec<LogicBlock> = Vec::new();
76    let mut block_map: HashMap<String, usize> = HashMap::new();
77
78    for (dir, mods) in &dir_groups {
79        let mut block_type = LogicBlockType::Util;
80        let mut default_name = dir.rsplit('/').next().unwrap_or(dir).to_string();
81
82        for pattern in &type_patterns {
83            if pattern.pattern.is_match(dir) {
84                block_type = pattern.block_type;
85                default_name = pattern.name.to_string();
86                break;
87            }
88        }
89
90        // 获取描述
91        let descriptions: Vec<String> = mods
92            .iter()
93            .filter_map(|m| m.semantic.as_ref().map(|s| s.description.clone()))
94            .collect();
95
96        let description = if !descriptions.is_empty() {
97            descriptions[0].clone()
98        } else if mods.len() > 3 {
99            let func_names: Vec<String> = mods
100                .iter()
101                .take(5)
102                .map(|m| {
103                    m.name
104                        .trim_end_matches(".ts")
105                        .trim_end_matches(".js")
106                        .to_string()
107                })
108                .collect();
109            format!("包含 {} 等 {} 个模块", func_names.join(", "), mods.len())
110        } else {
111            format!("{}相关功能", default_name)
112        };
113
114        let block = LogicBlock {
115            id: dir.clone(),
116            name: default_name,
117            description,
118            block_type,
119            files: mods.iter().map(|m| m.id.clone()).collect(),
120            file_count: mods.len(),
121            total_lines: mods.iter().map(|m| m.lines).sum(),
122            children: Vec::new(),
123            dependencies: Vec::new(),
124        };
125
126        block_map.insert(dir.clone(), blocks.len());
127        blocks.push(block);
128    }
129
130    // 建立块之间的依赖关系
131    for dep in &blueprint.references.module_deps {
132        let source_dir = get_dir(&dep.source);
133        let target_dir = get_dir(&dep.target);
134
135        if source_dir != target_dir {
136            if let Some(&source_idx) = block_map.get(&source_dir) {
137                if block_map.contains_key(&target_dir) {
138                    let block = &mut blocks[source_idx];
139                    if !block.dependencies.contains(&target_dir) {
140                        block.dependencies.push(target_dir);
141                    }
142                }
143            }
144        }
145    }
146
147    // 按类型和重要性排序
148    let type_order = |t: LogicBlockType| -> usize {
149        match t {
150            LogicBlockType::Entry => 0,
151            LogicBlockType::Core => 1,
152            LogicBlockType::Feature => 2,
153            LogicBlockType::Ui => 3,
154            LogicBlockType::Data => 4,
155            LogicBlockType::Config => 5,
156            LogicBlockType::Util => 6,
157        }
158    };
159
160    blocks.sort_by(|a, b| {
161        let order_a = type_order(a.block_type);
162        let order_b = type_order(b.block_type);
163        if order_a != order_b {
164            order_a.cmp(&order_b)
165        } else {
166            b.file_count.cmp(&a.file_count)
167        }
168    });
169
170    let project_desc = blueprint
171        .project
172        .semantic
173        .as_ref()
174        .map(|s| s.description.clone())
175        .unwrap_or_else(|| "项目描述".to_string());
176
177    ArchitectureMap {
178        project_name: blueprint.project.name.clone(),
179        project_description: project_desc,
180        blocks,
181    }
182}
183
184/// 将 SymbolKind 转换为字符串
185fn symbol_kind_to_string(kind: &crate::map::types_enhanced::SymbolKind) -> String {
186    use crate::map::types_enhanced::SymbolKind;
187    match kind {
188        SymbolKind::Function => "function",
189        SymbolKind::Class => "class",
190        SymbolKind::Method => "method",
191        SymbolKind::Property => "property",
192        SymbolKind::Variable => "variable",
193        SymbolKind::Constant => "constant",
194        SymbolKind::Interface => "interface",
195        SymbolKind::Type => "type",
196        SymbolKind::Enum => "enum",
197    }
198    .to_string()
199}
200
201/// 获取模块详情
202pub fn get_module_detail(
203    blueprint: &EnhancedCodeBlueprint,
204    module_id: &str,
205) -> Option<ModuleDetailInfo> {
206    let module = blueprint.modules.get(module_id)?;
207
208    let mut symbols = ModuleSymbols::default();
209
210    // 从全局符号表中查找属于此模块的符号
211    for symbol in blueprint.symbols.values() {
212        if symbol.module_id != module_id {
213            continue;
214        }
215
216        let kind_str = symbol_kind_to_string(&symbol.kind);
217        let info = SymbolInfo {
218            id: symbol.id.clone(),
219            name: symbol.name.clone(),
220            kind: kind_str.clone(),
221            signature: symbol.signature.clone(),
222            semantic: symbol
223                .semantic
224                .as_ref()
225                .map(|s| serde_json::to_value(s).unwrap_or_default()),
226            location: SymbolLocation {
227                start_line: symbol.location.start_line as usize,
228                end_line: symbol.location.end_line as usize,
229            },
230            children: Vec::new(), // TODO: 添加子符号
231        };
232
233        match kind_str.as_str() {
234            "class" => symbols.classes.push(info),
235            "interface" => symbols.interfaces.push(info),
236            "function" => symbols.functions.push(info),
237            "type" => symbols.types.push(info),
238            "variable" => symbols.variables.push(info),
239            "constant" => symbols.constants.push(info),
240            _ => symbols.functions.push(info),
241        }
242    }
243
244    // 解析导入
245    let mut external_imports: HashSet<String> = HashSet::new();
246    let mut internal_imports: HashSet<String> = HashSet::new();
247
248    for imp in &module.imports {
249        if imp.is_external {
250            external_imports.insert(imp.source.clone());
251        } else {
252            internal_imports.insert(imp.source.clone());
253        }
254    }
255
256    Some(ModuleDetailInfo {
257        id: module.id.clone(),
258        name: module.name.clone(),
259        path: module.path.clone(),
260        language: module.language.clone(),
261        lines: module.lines,
262        semantic: module
263            .semantic
264            .as_ref()
265            .map(|s| serde_json::to_value(s).unwrap_or_default()),
266        symbols,
267        external_imports: external_imports.into_iter().collect(),
268        internal_imports: internal_imports.into_iter().collect(),
269    })
270}
271
272/// 获取符号引用信息
273pub fn get_symbol_refs(
274    blueprint: &EnhancedCodeBlueprint,
275    symbol_id: &str,
276) -> Option<SymbolRefInfo> {
277    let symbol_entry = blueprint.symbols.get(symbol_id)?;
278
279    let mut refs = SymbolRefInfo {
280        symbol_id: symbol_id.to_string(),
281        symbol_name: symbol_entry.name.clone(),
282        symbol_kind: symbol_kind_to_string(&symbol_entry.kind),
283        module_id: symbol_entry.module_id.clone(),
284        called_by: Vec::new(),
285        calls: Vec::new(),
286        type_refs: Vec::new(),
287    };
288
289    // 从 blueprint.references.symbol_calls 中查找调用关系
290    for call in &blueprint.references.symbol_calls {
291        if call.callee == symbol_id {
292            let caller_symbol = blueprint.symbols.get(&call.caller);
293            refs.called_by.push(CallerInfo {
294                symbol_id: call.caller.clone(),
295                symbol_name: caller_symbol
296                    .map(|s| s.name.clone())
297                    .unwrap_or_else(|| call.caller.split("::").last().unwrap_or("").to_string()),
298                module_id: caller_symbol
299                    .map(|s| s.module_id.clone())
300                    .unwrap_or_default(),
301                call_type: call.call_type.clone(),
302                locations: call
303                    .locations
304                    .iter()
305                    .map(|loc| LineLocation {
306                        line: loc.start_line as usize,
307                    })
308                    .collect(),
309            });
310        }
311
312        if call.caller == symbol_id {
313            let callee_symbol = blueprint.symbols.get(&call.callee);
314            refs.calls.push(CallerInfo {
315                symbol_id: call.callee.clone(),
316                symbol_name: callee_symbol
317                    .map(|s| s.name.clone())
318                    .unwrap_or_else(|| call.callee.split("::").last().unwrap_or("").to_string()),
319                module_id: callee_symbol
320                    .map(|s| s.module_id.clone())
321                    .unwrap_or_default(),
322                call_type: call.call_type.clone(),
323                locations: call
324                    .locations
325                    .iter()
326                    .map(|loc| LineLocation {
327                        line: loc.start_line as usize,
328                    })
329                    .collect(),
330            });
331        }
332    }
333
334    // 查找类型引用(extends/implements)
335    for type_ref in &blueprint.references.type_refs {
336        if type_ref.child == symbol_id {
337            let parent_symbol = blueprint.symbols.get(&type_ref.parent);
338            refs.type_refs.push(TypeRefInfo {
339                related_symbol_id: type_ref.parent.clone(),
340                related_symbol_name: parent_symbol.map(|s| s.name.clone()).unwrap_or_default(),
341                kind: format!("{:?}", type_ref.kind).to_lowercase(),
342                direction: "parent".to_string(),
343            });
344        }
345        if type_ref.parent == symbol_id {
346            let child_symbol = blueprint.symbols.get(&type_ref.child);
347            refs.type_refs.push(TypeRefInfo {
348                related_symbol_id: type_ref.child.clone(),
349                related_symbol_name: child_symbol.map(|s| s.name.clone()).unwrap_or_default(),
350                kind: format!("{:?}", type_ref.kind).to_lowercase(),
351                direction: "child".to_string(),
352            });
353        }
354    }
355
356    Some(refs)
357}