1use std::collections::{HashMap, HashSet, VecDeque};
2use crate::graph::{Graph, Node};
3
4pub struct QueryEngine<'a> {
6 graph: &'a Graph,
7}
8
9impl<'a> QueryEngine<'a> {
10 pub fn new(graph: &'a Graph) -> Self {
11 Self { graph }
12 }
13
14 pub fn impact(&self, node_id: &str) -> Vec<&'a Node> {
17 let mut visited = HashSet::new();
18 let mut queue = VecDeque::new();
19 queue.push_back(node_id.to_string());
20 visited.insert(node_id.to_string());
21
22 while let Some(current) = queue.pop_front() {
23 for edge in &self.graph.edges {
25 if edge.to == current && edge.relation == "depends_on" {
26 if visited.insert(edge.from.clone()) {
27 queue.push_back(edge.from.clone());
28 }
29 }
30 }
31 }
32
33 visited.remove(node_id);
34 self.graph.nodes.iter()
35 .filter(|n| visited.contains(&n.id))
36 .collect()
37 }
38
39 pub fn deps(&self, node_id: &str, transitive: bool) -> Vec<&'a Node> {
41 if !transitive {
42 let dep_ids: HashSet<&str> = self.graph.edges.iter()
44 .filter(|e| e.from == node_id && e.relation == "depends_on")
45 .map(|e| e.to.as_str())
46 .collect();
47 return self.graph.nodes.iter()
48 .filter(|n| dep_ids.contains(n.id.as_str()))
49 .collect();
50 }
51
52 let mut visited = HashSet::new();
53 let mut queue = VecDeque::new();
54 queue.push_back(node_id.to_string());
55 visited.insert(node_id.to_string());
56
57 while let Some(current) = queue.pop_front() {
58 for edge in &self.graph.edges {
59 if edge.from == current && edge.relation == "depends_on" {
60 if visited.insert(edge.to.clone()) {
61 queue.push_back(edge.to.clone());
62 }
63 }
64 }
65 }
66
67 visited.remove(node_id);
68 self.graph.nodes.iter()
69 .filter(|n| visited.contains(&n.id))
70 .collect()
71 }
72
73 pub fn path(&self, from: &str, to: &str) -> Option<Vec<String>> {
75 let mut visited = HashSet::new();
76 let mut queue = VecDeque::new();
77 let mut parent: HashMap<String, String> = HashMap::new();
78
79 queue.push_back(from.to_string());
80 visited.insert(from.to_string());
81
82 while let Some(current) = queue.pop_front() {
83 if current == to {
84 let mut path = vec![to.to_string()];
86 let mut cur = to.to_string();
87 while let Some(p) = parent.get(&cur) {
88 path.push(p.clone());
89 cur = p.clone();
90 }
91 path.reverse();
92 return Some(path);
93 }
94
95 for edge in &self.graph.edges {
97 let neighbor = if edge.from == current {
98 &edge.to
99 } else if edge.to == current {
100 &edge.from
101 } else {
102 continue;
103 };
104 if visited.insert(neighbor.clone()) {
105 parent.insert(neighbor.clone(), current.clone());
106 queue.push_back(neighbor.clone());
107 }
108 }
109 }
110
111 None
112 }
113
114 pub fn common_cause(&self, node_a: &str, node_b: &str) -> Vec<&'a Node> {
116 let deps_a: HashSet<String> = self.deps(node_a, true)
117 .iter().map(|n| n.id.clone()).collect();
118 let deps_b: HashSet<String> = self.deps(node_b, true)
119 .iter().map(|n| n.id.clone()).collect();
120 let common: HashSet<&String> = deps_a.intersection(&deps_b).collect();
121
122 self.graph.nodes.iter()
123 .filter(|n| common.contains(&n.id))
124 .collect()
125 }
126
127 pub fn topological_sort(&self) -> anyhow::Result<Vec<String>> {
129 let mut in_degree: HashMap<&str, usize> = HashMap::new();
130 for node in &self.graph.nodes {
131 in_degree.entry(&node.id).or_insert(0);
132 }
133 for edge in &self.graph.edges {
134 if edge.relation == "depends_on" {
135 *in_degree.entry(&edge.from).or_insert(0) += 1;
136 }
137 }
138
139 let mut queue: VecDeque<&str> = in_degree.iter()
140 .filter(|(_, °)| deg == 0)
141 .map(|(&id, _)| id)
142 .collect();
143
144 let mut sorted = Vec::new();
145 while let Some(node) = queue.pop_front() {
146 sorted.push(node.to_string());
147 for edge in &self.graph.edges {
148 if edge.to == node && edge.relation == "depends_on" {
149 if let Some(deg) = in_degree.get_mut(edge.from.as_str()) {
150 *deg -= 1;
151 if *deg == 0 {
152 queue.push_back(&edge.from);
153 }
154 }
155 }
156 }
157 }
158
159 if sorted.len() != self.graph.nodes.len() {
160 anyhow::bail!("Cycle detected in graph");
161 }
162
163 Ok(sorted)
164 }
165}