impl DependencyGraphBuilder {
fn analyze_file(&mut self, path: &Path) -> Result<NodeId> {
let content = fs::read_to_string(path)?;
let hash = self.calculate_hash(&content);
if let Some(&existing_hash) = self.processed_hashes.get(path) {
if existing_hash == hash {
return Ok(*self.node_map.get(path).expect(
"path exists in node_map (inserted at line 184 when added to processed_hashes)",
));
}
}
let node_data = NodeData {
path: path.to_path_buf(),
module: self.path_to_module(path),
symbols: self
.symbol_table
.get_file_symbols(path)
.iter()
.map(|e| e.symbol.clone())
.collect(),
loc: content.lines().count(),
complexity: self.estimate_complexity(&content),
ast_hash: hash,
};
let node_id = if let Some(&existing_id) = self.node_map.get(path) {
*self
.graph
.node_weight_mut(existing_id)
.expect("node exists in graph (node_id came from node_map at line 184)") =
node_data;
existing_id
} else {
let id = self.graph.add_node(node_data);
self.node_map.insert(path.to_path_buf(), id);
id
};
self.processed_hashes.insert(path.to_path_buf(), hash);
Ok(node_id)
}
fn extract_function_name(line: &str) -> Option<&str> {
line.split_whitespace()
.find(|&w| w != "pub" && w != "fn")
.and_then(|s| s.split('(').next())
}
fn extract_type_name<'a>(line: &'a str, keyword: &str) -> Option<&'a str> {
line.split_whitespace()
.find(|&w| w != "pub" && w != keyword)
.and_then(|s| s.split('{').next())
.and_then(|s| s.split('<').next())
}
fn extract_python_function_name(line: &str) -> Option<&str> {
line.strip_prefix("def ")
.and_then(|s| s.split('(').next())
.map(|s| s.trim())
}
fn extract_python_class_name(line: &str) -> Option<&str> {
line.strip_prefix("class ")
.and_then(|s| s.split('(').next())
.and_then(|s| s.split(':').next())
.map(|s| s.trim())
}
fn extract_ts_name(line: &str) -> Option<&str> {
line.split_whitespace()
.find(|&w| w != "export" && w != "const" && w != "function")
.and_then(|s| s.split('(').next())
.and_then(|s| s.split('=').next())
.map(|s| s.trim())
}
fn extract_ts_class_name(line: &str) -> Option<&str> {
line.split_whitespace()
.find(|&w| w != "export" && w != "class")
.and_then(|s| s.split('{').next())
.map(|s| s.trim())
}
fn path_to_module(&self, path: &Path) -> String {
path.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown")
.to_string()
}
fn calculate_hash(&self, content: &str) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
hasher.finish()
}
fn estimate_complexity(&self, content: &str) -> f64 {
let mut complexity = 1.0;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("if ") || trimmed.starts_with("else") {
complexity += 1.0;
} else if trimmed.starts_with("for ") || trimmed.starts_with("while ") {
complexity += 2.0;
} else if trimmed.starts_with("match ") || trimmed.starts_with("switch ") {
complexity += 1.5;
}
}
complexity
}
fn resolve_import_to_node(&self, import: &str) -> Option<NodeId> {
for (path, &node_id) in &self.node_map {
let module_name = self.path_to_module(path);
if import.contains(&module_name) {
return Some(node_id);
}
}
None
}
}