1pub extern crate petgraph;
7use fnv::FnvHashMap;
8
9use std::collections::HashSet;
10
11use crate::db::graph::petgraph::visit::Walker;
12use crate::db::table;
13use crate::db::DB;
14use std::fmt;
15
16#[derive(Clone)]
24pub enum NodeType {
25 Required,
26 Optional,
27}
28
29impl fmt::Debug for NodeType {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 match self {
33 NodeType::Required => write!(f, "Required Node"),
34 NodeType::Optional => write!(f, "Optional Node"),
35 }
36 }
37}
38
39#[derive(Debug)]
42pub struct Graph<'a> {
43 _graph: petgraph::Graph<NodeType, String>,
44 _name_map: FnvHashMap<String, petgraph::graph::NodeIndex<petgraph::graph::DefaultIx>>,
45 _index_map: FnvHashMap<petgraph::graph::NodeIndex<petgraph::graph::DefaultIx>, String>,
46 _db: &'a DB,
47 _processed: HashSet<String>,
48}
49
50impl<'a> Graph<'a> {
51 pub fn new(db: &'a DB) -> Graph {
53 Graph {
54 _graph: petgraph::Graph::<NodeType, String>::new(),
55 _name_map: FnvHashMap::default(),
56 _index_map: FnvHashMap::default(),
57 _db: db,
58 _processed: HashSet::new(),
59 }
60 }
61 pub fn get_name(
63 &self,
64 number: petgraph::graph::NodeIndex<petgraph::graph::DefaultIx>,
65 ) -> String {
66 return self._index_map[&number].clone();
67 }
68
69 pub fn add_or_update_product(&mut self, name: String, node_type: NodeType) {
71 match self.has_product(&name) {
72 true => {
73 let name_index = self._name_map[&name];
74 if let (&NodeType::Optional, NodeType::Required) =
75 (self._graph.node_weight(name_index).unwrap(), node_type)
76 {
77 self._graph[name_index] = NodeType::Required;
78 }
79 }
80 false => {
81 let index = self._graph.add_node(node_type);
82 self._name_map.insert(name.clone(), index);
83 self._index_map.insert(index, name);
84 }
85 }
86 }
87
88 pub fn has_product(&self, name: &String) -> bool {
90 self._name_map.contains_key(name)
91 }
92
93 pub fn product_versions(&self, name: &String) -> Vec<&String> {
97 let mut products = Vec::new();
98 let index = self._name_map[name];
99 let direction = petgraph::Direction::Incoming;
100 for edge in self._graph.edges_directed(index, direction) {
101 products.push(edge.weight());
102 }
103 products
104 }
105
106 pub fn is_optional(&self, name: &String) -> bool {
108 let node = self._name_map[name];
109 let weight = self._graph.node_weight(node);
110 match weight {
111 Some(NodeType::Optional) => true,
112 _ => false,
113 }
114 }
115
116 pub fn connect_products(
120 &mut self,
121 source: &String,
122 target: &String,
123 version: String,
124 ) -> Result<(), &str> {
125 if !self.has_product(source) {
126 return Err("The specified source is not in the graph");
127 }
128 if !self.has_product(target) {
129 return Err("The specified target is not in the graph");
130 }
131 let source_index = self._name_map[source];
132 let target_index = self._name_map[target];
133 self._graph.add_edge(source_index, target_index, version);
134 Ok(())
135 }
136
137 pub fn add_product_by_tag(
140 &mut self,
141 product: String,
142 tag: &Vec<&String>,
143 version_type: table::VersionType,
144 node_type: NodeType,
145 recurse: bool,
146 ) {
147 if !self._processed.contains(&product) {
148 let result = self._db.get_table_from_tag(&product, tag.clone());
149 if let Some(table) = result {
150 self.add_table(&table, version_type, node_type, Some(tag), recurse);
151 }
152 }
153 }
154
155 pub fn add_product_by_version(
159 &mut self,
160 product: String,
161 version: String,
162 version_type: table::VersionType,
163 node_type: NodeType,
164 recurse: bool,
165 ) {
166 if !self._processed.contains(&product) {
167 let result = self._db.get_table_from_version(&product, &version);
168 if let Some(table) = result {
169 self.add_table(&table, version_type, node_type, None, recurse);
170 }
171 }
172 }
173
174 pub fn add_table(
177 &mut self,
178 table: &table::Table,
179 version_type: table::VersionType,
180 node_type: NodeType,
181 tag: Option<&Vec<&String>>,
182 recurse: bool,
183 ) {
184 let top = &table.name;
185
186 self.add_or_update_product(top.clone(), node_type);
187
188 let dependencies = match version_type {
189 table::VersionType::Exact => table.exact.as_ref(),
190 table::VersionType::Inexact => table.inexact.as_ref(),
191 };
192 if let None = dependencies {
195 return;
196 }
197 let dep_unwrap = dependencies.unwrap();
200 for (dep_vec, node_type) in vec![&dep_unwrap.required, &dep_unwrap.optional]
201 .iter()
202 .zip(vec![NodeType::Required, NodeType::Optional])
203 {
204 for (k, v) in dep_vec.iter() {
205 self.add_or_update_product(k.clone(), node_type.clone());
206 if let Err(_) = self.connect_products(top, &k, v.clone()) {
207 eprintln!("There was an issue connecting products in the graph, topological walks my be incorrect");
208 }
209
210 match (&version_type, tag, recurse) {
211 (table::VersionType::Inexact, Some(tag_vec), true) => self.add_product_by_tag(
212 k.clone(),
213 tag_vec,
214 table::VersionType::Inexact,
215 node_type.clone(),
216 recurse,
217 ),
218 (table::VersionType::Exact, _, true) => self.add_product_by_version(
219 k.clone(),
220 v.clone(),
221 table::VersionType::Exact,
222 node_type.clone(),
223 recurse,
224 ),
225 _ => {}
226 }
227 }
228 }
229 self._processed.insert(top.clone());
230 }
231
232 pub fn iter(
234 &self,
235 ) -> petgraph::visit::WalkerIter<
236 petgraph::visit::Topo<
237 <petgraph::Graph<NodeType, String> as petgraph::visit::GraphBase>::NodeId,
238 <petgraph::Graph<NodeType, String> as petgraph::visit::Visitable>::Map,
239 >,
240 &petgraph::Graph<NodeType, String>,
241 > {
242 let topo = petgraph::visit::Topo::new(&self._graph);
243 return topo.iter(&self._graph);
244 }
245}