@@ 74,7 74,6 @@ dependencies = [
"regex",
"serde",
"serde_yaml",
- "topological-sort",
"walkdir",
]
@@ 356,12 355,6 @@ dependencies = [
]
[[package]]
-name = "topological-sort"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
-
-[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 1,6 1,4 @@
-use std::{path::PathBuf, collections::{LinkedList, HashSet}};
-
-use topological_sort::TopologicalSort;
+use std::{path::PathBuf, collections::{LinkedList, HashSet, HashMap}};
use crate::types::{PackageName, Database};
@@ 26,7 24,7 @@ fn resolve_package(name: &str, db: &Database) -> HashSet<PackageName> {
/// creates a list of all dependencies used by the package itself,
/// or by packages required by the start package
-fn list_all_deps(start: PackageName, db: &Database, local_only: bool) -> HashSet<PackageName> {
+fn list_all_deps(start: PackageName, db: &Database) -> HashSet<PackageName> {
let mut stack = LinkedList::new();
let mut marker = HashSet::new();
stack.push_front(start);
@@ 43,14 41,20 @@ fn list_all_deps(start: PackageName, db: &Database, local_only: bool) -> HashSet
if res.is_empty() {
// not a local dependency,
// so we can't go any deeper
- if !local_only {
- marker.insert(current);
- }
+ marker.insert(current);
} else {
for loc in res {
+ if marker.contains(&loc) {
+ // already processed vertex
+ continue;
+ }
+
if let Some(deps) = db.depends.get(&loc) {
for dep in deps {
- stack.push_front(dep.to_string());
+ let resolved = resolve_package(dep, db);
+ for res in resolved {
+ stack.push_front(res.to_string());
+ }
}
}
@@ 64,7 68,6 @@ fn list_all_deps(start: PackageName, db: &Database, local_only: bool) -> HashSet
}
}
}
-
}
return marker;
@@ 72,84 75,131 @@ fn list_all_deps(start: PackageName, db: &Database, local_only: bool) -> HashSet
/// topologically sorts all local dependencies
/// filters unknown dependencies
-pub fn topological_sort(start: &PackageName, db: &Database) -> Option<Vec<PackageName>> {
- let mut ts = {
- let mut stack = LinkedList::new();
- let mut marker = HashSet::new();
- stack.push_front(start.to_string());
+pub fn topological_sort(start: &PackageName, db: &Database) -> Result<Vec<PackageName>, Vec<(PackageName, PackageName)>> {
+ let mut stack = LinkedList::new();
+ let mut marker = HashSet::new();
+ stack.push_front(start.to_string());
+ let mut edge_out = HashMap::new();
+ let mut edge_in = HashMap::new();
- let mut ts = TopologicalSort::<String>::new();
- ts.insert(start);
+ while !stack.is_empty() {
+ let node = stack.pop_back().unwrap();
- // setup the topological sort dependency graph
- while ! stack.is_empty() {
- let current = stack.pop_back().unwrap();
+ if marker.contains(&node) {
+ // already processed node
+ continue;
+ }
- if marker.contains(¤t) {
- // already processed vertex
+ for provider in resolve_package(&node, db) {
+ if marker.contains(&provider) {
+ // already processed node
continue;
}
- let res = resolve_package(¤t, db);
- if !res.is_empty() {
- for loc in res {
- if let Some(deps) = db.depends.get(&loc) {
- for dep in deps {
- let resolved = resolve_package(dep, db);
- for res in resolved {
- stack.push_front(res.to_string());
- ts.add_dependency(
- res.to_string(),
- current.to_string()
- );
- }
+ if let Some(deps) = db.depends.get(&provider) {
+ for dep in deps {
+ let resolved = resolve_package(dep, db);
+ for res in resolved {
+ stack.push_front(res.to_string());
+
+ if res == provider {
+ continue;
}
- }
- // if the current package is provided by the given package,
- // we add the package name of the provider-package
- // to our list
- if let Some(pkg) = db.packages.get(&loc) {
- if pkg.provides.contains(¤t) || pkg.name == current {
- marker.insert(pkg.name.to_string());
+ if !edge_out.contains_key(&provider) {
+ edge_out.insert(provider.to_string(), HashSet::new());
+ }
+ edge_out.get_mut(&provider).unwrap()
+ .insert(res.to_string());
+ if !edge_in.contains_key(&res) {
+ edge_in.insert(res.to_string(), HashSet::new());
}
+ edge_in.get_mut(&res).unwrap()
+ .insert(provider.to_string());
+
}
}
}
+
+ // if the current package is provided by the given package,
+ // we add the package name of the provider-package
+ // to our list
+ if let Some(pkg) = db.packages.get(&provider) {
+ if pkg.provides.contains(&node) || pkg.name == node {
+ marker.insert(pkg.name.to_string());
+ }
+ }
+
+ }
+ }
+
+ let mut sources = Vec::new();
+ // generate a list of all sinks
+ // (packages that do not require other packages)
+ for node in &marker {
+ if !edge_out.contains_key(node) {
+ sources.push(node.to_string());
}
+ }
+
+ let mut order = Vec::new();
- ts
- };
+ while !sources.is_empty() {
+ let node = sources.pop().unwrap();
+ order.push(node.to_string());
- let mut v = Vec::new();
+ for out in edge_in.get(&node).unwrap_or(&HashSet::new()).clone() {
+ // remove connection from node to out
+ if let Some(list) = edge_in.get_mut(&node) {
+ list.remove(&out);
+ if list.is_empty() {
+ // turned into a sink
+ sources.push(out.to_string());
+
+ edge_in.remove(&node);
+ }
+ }
+ }
+ }
- while !ts.is_empty() {
- let mut next = ts.pop_all();
+ if !edge_in.is_empty() {
+ let mut v = Vec::new();
- if next.is_empty() {
- // cycle detected
- return None;
+ for (node, connections) in edge_in {
+ for con in connections {
+ v.push((node.to_string(), con));
+ }
}
- v.append(&mut next);
+ return Err(v);
}
- Some(v)
+ Ok(order)
}
+/// Command used to list packages required to build the given package
+/// will print the topological order, when local_only is set
pub fn tree(name: PackageName, db: Option<PathBuf>, local_only: bool) {
let db = Database::from_file(db.unwrap_or(PathBuf::from("db.yml")))
.expect("Unable to open database");
if local_only {
- let sorted = topological_sort(&name, &db)
- .expect("Unable to resolve dependencies, as there is a cycle in the graph");
- for dep in sorted {
- println!("{}", dep);
+ match topological_sort(&name, &db) {
+ Ok(sorted) => {
+ for dep in sorted {
+ println!("{}", dep);
+ }
+ },
+ Err(cycle) => {
+ println!("Cycle detected");
+ for dep in cycle {
+ println!("{}->{}", dep.1, dep.0);
+ }
+ }
}
} else {
- let deps = list_all_deps(name, &db, false);
+ let deps = list_all_deps(name, &db);
for dep in deps {
println!("{}", dep);
}
@@ 91,7 91,9 @@ impl Database {
};
db.packages.insert(apk.name.to_string(), pkg);
- db.depends.insert(apk.name.to_string(), depends);
+ if !depends.is_empty() {
+ db.depends.insert(apk.name.to_string(), depends);
+ }
for provider in provides {
if let Some(table) = db.provides.get_mut(&provider) {