project_map_cli_rust/core/
query_engine.rs1use crate::core::graph::{ProjectGraph, NodeData, NodeType};
2use crate::error::Result;
3use petgraph::visit::Dfs;
4use std::path::Path;
5
6pub struct QueryEngine {
7 graph: ProjectGraph,
8}
9
10impl QueryEngine {
11 pub fn load(path: &Path) -> Result<Self> {
12 let graph = ProjectGraph::load(path)?;
13 Ok(Self { graph })
14 }
15
16 pub fn find_symbols(&self, query: &str) -> Vec<NodeData> {
17 let query_lower = query.to_lowercase();
18 self.graph.graph.node_weights()
19 .filter(|n| n.node_type == NodeType::Symbol && n.name.to_lowercase().contains(&query_lower))
20 .cloned()
21 .collect()
22 }
23
24 pub fn get_file_outline(&self, path: &str) -> Vec<NodeData> {
25 let file_node = self.graph.graph.node_indices()
26 .find(|i| self.graph.graph[*i].node_type == NodeType::File && self.graph.graph[*i].path == path);
27
28 if let Some(idx) = file_node {
29 self.graph.graph.neighbors_directed(idx, petgraph::Direction::Outgoing)
30 .map(|n| self.graph.graph[n].clone())
31 .collect()
32 } else {
33 Vec::new()
34 }
35 }
36
37 pub fn analyze_impact(&self, name: &str) -> Vec<NodeData> {
38 let node_idx = self.graph.graph.node_indices()
39 .find(|i| self.graph.graph[*i].name == name);
40
41 if let Some(start_node) = node_idx {
42 let mut dfs = Dfs::new(&self.graph.graph, start_node);
44 let mut results = Vec::new();
45 while let Some(nx) = dfs.next(&self.graph.graph) {
46 if nx != start_node {
47 results.push(self.graph.graph[nx].clone());
48 }
49 }
50 results
51 } else {
52 Vec::new()
53 }
54 }
55
56 pub fn check_blast_radius(&self, path: &str, symbol: &str) -> Vec<NodeData> {
57 let node_idx = self.graph.graph.node_indices()
58 .find(|i| {
59 let node = &self.graph.graph[*i];
60 node.path == path && node.name == symbol && node.node_type == NodeType::Symbol
61 });
62
63 let start_node = if let Some(idx) = node_idx {
64 idx
65 } else {
66 let file_node = self.graph.graph.node_indices()
68 .find(|i| {
69 let node = &self.graph.graph[*i];
70 node.path == path && node.node_type == NodeType::File
71 });
72 if let Some(idx) = file_node { idx } else { return Vec::new(); }
73 };
74
75 let mut results = Vec::new();
81 let mut stack = vec![start_node];
82 let mut visited = std::collections::HashSet::new();
83 visited.insert(start_node);
84
85 while let Some(current) = stack.pop() {
86 for neighbor in self.graph.graph.neighbors_directed(current, petgraph::Direction::Incoming) {
87 if visited.insert(neighbor) {
88 results.push(self.graph.graph[neighbor].clone());
89 stack.push(neighbor);
90 }
91 }
92 }
93
94 results
95 }
96
97 pub fn find_symbol_in_path(&self, path: &str, name: &str) -> Option<NodeData> {
98 self.graph.graph.node_weights()
99 .find(|n| n.node_type == NodeType::Symbol && n.path == path && n.name == name)
100 .cloned()
101 }
102}