Skip to main content

aster/map/
dependency_analyzer.rs

1//! 依赖分析器
2//!
3//! 分析模块之间的导入/依赖关系
4
5use std::collections::{HashMap, HashSet};
6use std::path::Path;
7
8use super::types::{DependencyEdge, DependencyGraph, DependencyType, ModuleNode};
9
10/// 解析配置
11struct ResolutionConfig {
12    extensions: Vec<&'static str>,
13    index_files: Vec<&'static str>,
14}
15
16fn get_resolution_config(language: &str) -> ResolutionConfig {
17    match language {
18        "typescript" => ResolutionConfig {
19            extensions: vec![".ts", ".tsx", ".d.ts", ".js", ".jsx", ""],
20            index_files: vec!["index.ts", "index.tsx", "index.js", "index.jsx"],
21        },
22        "javascript" => ResolutionConfig {
23            extensions: vec![".js", ".jsx", ".mjs", ".cjs", ""],
24            index_files: vec!["index.js", "index.jsx", "index.mjs"],
25        },
26        "python" => ResolutionConfig {
27            extensions: vec![".py", ""],
28            index_files: vec!["__init__.py"],
29        },
30        _ => ResolutionConfig {
31            extensions: vec![""],
32            index_files: vec![],
33        },
34    }
35}
36
37/// 依赖分析器
38pub struct DependencyAnalyzer {
39    module_index: HashMap<String, ModuleNode>,
40    module_ids: HashSet<String>,
41}
42
43impl DependencyAnalyzer {
44    pub fn new() -> Self {
45        Self {
46            module_index: HashMap::new(),
47            module_ids: HashSet::new(),
48        }
49    }
50
51    /// 分析模块间的依赖关系
52    pub fn analyze_dependencies(&mut self, modules: &[ModuleNode]) -> DependencyGraph {
53        self.build_module_index(modules);
54        let mut edges = Vec::new();
55
56        for module in modules {
57            self.analyze_module_dependencies(module, &mut edges);
58        }
59
60        DependencyGraph { edges }
61    }
62
63    /// 建立模块索引
64    fn build_module_index(&mut self, modules: &[ModuleNode]) {
65        self.module_index.clear();
66        self.module_ids.clear();
67
68        for module in modules {
69            self.module_index.insert(module.id.clone(), module.clone());
70            self.module_ids.insert(module.id.clone());
71
72            // 也索引不带扩展名的路径
73            if let Some(pos) = module.id.rfind('.') {
74                let without_ext = module.id.get(..pos).unwrap_or(&module.id);
75                self.module_index
76                    .insert(without_ext.to_string(), module.clone());
77            }
78        }
79    }
80
81    /// 分析单个模块的依赖
82    fn analyze_module_dependencies(&self, module: &ModuleNode, edges: &mut Vec<DependencyEdge>) {
83        for imp in &module.imports {
84            if let Some(target_id) =
85                self.resolve_import_target(&imp.source, &module.id, &module.language)
86            {
87                edges.push(DependencyEdge {
88                    source: module.id.clone(),
89                    target: target_id,
90                    edge_type: if imp.is_dynamic {
91                        DependencyType::Dynamic
92                    } else {
93                        DependencyType::Import
94                    },
95                    symbols: imp.symbols.clone(),
96                    is_type_only: self.is_type_only_import(imp),
97                });
98            }
99        }
100    }
101
102    /// 解析导入目标模块
103    fn resolve_import_target(&self, source: &str, current_id: &str, lang: &str) -> Option<String> {
104        // 跳过外部依赖
105        if !source.starts_with('.') && !source.starts_with('/') {
106            return None;
107        }
108
109        let current_dir = Path::new(current_id).parent()?.to_str()?;
110        let target_path = self.normalize_path(&format!("{}/{}", current_dir, source));
111        let config = get_resolution_config(lang);
112
113        // 尝试各种扩展名
114        for ext in &config.extensions {
115            let candidate = format!("{}{}", target_path, ext);
116            if self.module_ids.contains(&candidate) {
117                return Some(candidate);
118            }
119        }
120
121        // 尝试 index 文件
122        for index_file in &config.index_files {
123            let candidate = format!("{}/{}", target_path, index_file);
124            if self.module_ids.contains(&candidate) {
125                return Some(candidate);
126            }
127        }
128
129        None
130    }
131
132    /// 规范化路径
133    fn normalize_path(&self, p: &str) -> String {
134        let parts: Vec<&str> = p.split('/').collect();
135        let mut result = Vec::new();
136
137        for part in parts {
138            match part {
139                ".." => {
140                    result.pop();
141                }
142                "." | "" => {}
143                _ => result.push(part),
144            }
145        }
146
147        result.join("/")
148    }
149
150    /// 判断是否为纯类型导入
151    fn is_type_only_import(&self, imp: &super::types::ImportInfo) -> bool {
152        imp.symbols.iter().any(|s| s.starts_with("type "))
153    }
154
155    /// 检测循环依赖
156    pub fn detect_circular_dependencies(&self, graph: &DependencyGraph) -> Vec<Vec<String>> {
157        let mut cycles = Vec::new();
158        let mut adjacency: HashMap<String, Vec<String>> = HashMap::new();
159
160        for edge in &graph.edges {
161            adjacency
162                .entry(edge.source.clone())
163                .or_default()
164                .push(edge.target.clone());
165        }
166
167        let mut visited = HashSet::new();
168        let mut rec_stack = HashSet::new();
169        let mut path = Vec::new();
170
171        for node in adjacency.keys() {
172            if !visited.contains(node) {
173                self.dfs_cycle(
174                    node,
175                    &adjacency,
176                    &mut visited,
177                    &mut rec_stack,
178                    &mut path,
179                    &mut cycles,
180                );
181            }
182        }
183
184        cycles
185    }
186
187    fn dfs_cycle(
188        &self,
189        node: &str,
190        adj: &HashMap<String, Vec<String>>,
191        visited: &mut HashSet<String>,
192        rec_stack: &mut HashSet<String>,
193        path: &mut Vec<String>,
194        cycles: &mut Vec<Vec<String>>,
195    ) {
196        visited.insert(node.to_string());
197        rec_stack.insert(node.to_string());
198        path.push(node.to_string());
199
200        if let Some(neighbors) = adj.get(node) {
201            for neighbor in neighbors {
202                if !visited.contains(neighbor) {
203                    self.dfs_cycle(neighbor, adj, visited, rec_stack, path, cycles);
204                } else if rec_stack.contains(neighbor) {
205                    if let Some(start) = path.iter().position(|x| x == neighbor) {
206                        let mut cycle: Vec<String> = path[start..].to_vec();
207                        cycle.push(neighbor.clone());
208                        cycles.push(cycle);
209                    }
210                }
211            }
212        }
213
214        path.pop();
215        rec_stack.remove(node);
216    }
217
218    /// 获取依赖统计信息
219    pub fn get_dependency_stats(&self, graph: &DependencyGraph) -> DependencyStats {
220        let mut dependent_count: HashMap<String, usize> = HashMap::new();
221        let mut depended_count: HashMap<String, usize> = HashMap::new();
222        let mut type_only = 0;
223        let mut dynamic = 0;
224
225        for edge in &graph.edges {
226            *dependent_count.entry(edge.source.clone()).or_insert(0) += 1;
227            *depended_count.entry(edge.target.clone()).or_insert(0) += 1;
228            if edge.is_type_only {
229                type_only += 1;
230            }
231            if edge.edge_type == DependencyType::Dynamic {
232                dynamic += 1;
233            }
234        }
235
236        let mut most_dependent: Vec<_> = dependent_count.into_iter().collect();
237        most_dependent.sort_by(|a, b| b.1.cmp(&a.1));
238
239        let mut most_depended: Vec<_> = depended_count.into_iter().collect();
240        most_depended.sort_by(|a, b| b.1.cmp(&a.1));
241
242        DependencyStats {
243            total_edges: graph.edges.len(),
244            type_only_deps: type_only,
245            dynamic_deps: dynamic,
246            most_dependent: most_dependent.into_iter().take(10).collect(),
247            most_depended: most_depended.into_iter().take(10).collect(),
248        }
249    }
250}
251
252impl Default for DependencyAnalyzer {
253    fn default() -> Self {
254        Self::new()
255    }
256}
257
258/// 依赖统计
259#[derive(Debug, Clone, Default)]
260pub struct DependencyStats {
261    pub total_edges: usize,
262    pub type_only_deps: usize,
263    pub dynamic_deps: usize,
264    pub most_dependent: Vec<(String, usize)>,
265    pub most_depended: Vec<(String, usize)>,
266}
267
268/// 便捷函数:分析依赖
269pub fn analyze_dependencies(modules: &[ModuleNode]) -> DependencyGraph {
270    let mut analyzer = DependencyAnalyzer::new();
271    analyzer.analyze_dependencies(modules)
272}