deepwoken_reqparse/util/
reqtree.rs1use std::collections::{HashMap, HashSet, VecDeque};
2
3use crate::req::Requirement;
4
5pub struct ReqTree {
6 reqs: HashMap<String, Requirement>,
8 dependents: HashMap<String, HashSet<String>>,
10}
11
12impl ReqTree {
13 pub fn new() -> Self {
14 Self {
15 reqs: HashMap::new(),
16 dependents: HashMap::new(),
17 }
18 }
19
20 pub fn insert(&mut self, req: Requirement) {
22 let name = req.name_or_default();
23
24 for prereq in &req.prereqs {
25 self.dependents
26 .entry(prereq.clone())
27 .or_default()
28 .insert(name.clone());
29 }
30
31 self.reqs.insert(name, req);
32 }
33
34 pub fn get(&self, name: &str) -> Option<&Requirement> {
35 self.reqs.get(name)
36 }
37
38 pub fn prereqs(&self, name: &str) -> Option<&[String]> {
40 self.reqs.get(name).map(|r| r.prereqs.as_slice())
41 }
42
43 pub fn dependents(&self, name: &str) -> Option<&HashSet<String>> {
45 self.dependents.get(name)
46 }
47
48 pub fn all_prereqs(&self, name: &str) -> HashSet<String> {
50 let mut visited = HashSet::new();
51 let mut queue = VecDeque::new();
52
53 if let Some(req) = self.reqs.get(name) {
54 queue.extend(req.prereqs.iter().cloned());
55 }
56
57 while let Some(current) = queue.pop_front() {
58 if visited.insert(current.clone()) {
59 if let Some(req) = self.reqs.get(¤t) {
60 queue.extend(req.prereqs.iter().cloned());
61 }
62 }
63 }
64
65 visited
66 }
67
68 pub fn all_dependents(&self, name: &str) -> HashSet<String> {
70 let mut visited = HashSet::new();
71 let mut queue = VecDeque::new();
72
73 if let Some(deps) = self.dependents.get(name) {
74 queue.extend(deps.iter().cloned());
75 }
76
77 while let Some(current) = queue.pop_front() {
78 if visited.insert(current.clone()) {
79 if let Some(deps) = self.dependents.get(¤t) {
80 queue.extend(deps.iter().cloned());
81 }
82 }
83 }
84
85 visited
86 }
87
88 pub fn find_cycle(&self) -> Option<Vec<String>> {
90 let mut visited = HashSet::new();
91 let mut stack = HashSet::new();
92 let mut path = Vec::new();
93
94 for name in self.reqs.keys() {
95 if let Some(cycle) = self.cycle_visit(
96 name,
97 &mut visited,
98 &mut stack,
99 &mut path
100 ) {
101 return Some(cycle);
102 }
103 }
104 None
105 }
106
107 fn cycle_visit(
108 &self,
109 name: &str,
110 visited: &mut HashSet<String>,
111 stack: &mut HashSet<String>,
112 path: &mut Vec<String>,
113 ) -> Option<Vec<String>> {
114 if stack.contains(name) {
115 let idx = path.iter()
116 .position(|n| n == name).unwrap();
117
118 return Some(path[idx..].to_vec());
119 }
120 if visited.contains(name) {
121 return None;
122 }
123
124 visited.insert(name.to_string());
125 stack.insert(name.to_string());
126 path.push(name.to_string());
127
128 if let Some(req) = self.reqs.get(name) {
129 for prereq in &req.prereqs {
130 if let Some(cycle) = self.cycle_visit(
131 prereq,
132 visited,
133 stack,
134 path
135 ) {
136 return Some(cycle);
137 }
138 }
139 }
140
141 stack.remove(name);
142 path.pop();
143 None
144 }
145}