pub mod error;
pub use error::SolventError;
use std::collections::{HashMap,HashSet};
use std::collections::hash_map::Entry;
use std::iter::{Iterator};
#[derive(Debug,Clone)]
pub struct DepGraph<T: Eq> {
nodes: Vec<T>,
dependencies: HashMap<usize, HashSet<usize>>,
satisfied: HashSet<usize>,
}
impl<T: Eq> DepGraph<T> {
pub fn new() -> DepGraph<T> {
DepGraph {
nodes: Vec::new(),
dependencies: HashMap::new(),
satisfied: HashSet::new(),
}
}
fn _pos(&self, node: &T) -> Option<usize> {
self.nodes.iter().position(|x| x==node)
}
fn _register_node(&mut self, node: T) -> usize {
match self._pos(&node) {
Some(pos) => pos,
None => {
self.nodes.push(node);
self.nodes.len() - 1
}
}
}
pub fn register_nodes(&mut self, mut nodes: Vec<T>) {
for node in nodes.drain(..) {
self.register_node(node);
}
}
pub fn register_node(&mut self, node: T) {
self._register_node(node);
}
pub fn register_dependency(&mut self, node: T, depends_on: T) {
let node_pos = self._register_node(node);
let dep_pos = self._register_node(depends_on);
match self.dependencies.entry( node_pos ) {
Entry::Vacant(entry) => {
let mut deps = HashSet::with_capacity(1);
deps.insert( dep_pos );
entry.insert( deps );
},
Entry::Occupied(mut entry) => {
(*entry.get_mut()).insert( dep_pos );
},
}
}
pub fn register_dependencies(&mut self, node: T, mut depends_on: Vec<T>)
{
let node_pos = self._register_node(node);
let mut dep_poses: Vec<usize> = Vec::new();
for dp in depends_on.drain(..) {
let pos = self._register_node(dp);
dep_poses.push(pos);
}
match self.dependencies.entry( node_pos ) {
Entry::Vacant(entry) => {
let mut deps: HashSet<usize> = HashSet::with_capacity( dep_poses.len() );
for pos in dep_poses.iter() {
deps.insert( *pos );
}
entry.insert( deps );
},
Entry::Occupied(mut entry) => {
for pos in dep_poses.iter() {
(*entry.get_mut()).insert( *pos );
}
},
}
}
pub fn mark_as_satisfied(&mut self, nodes: &[T]) -> Result<(), SolventError>
{
for node in nodes.iter() {
let node_pos = match self._pos(node) {
None => return Err(SolventError::NoSuchNode),
Some(pos) => pos
};
self.satisfied.insert( node_pos );
}
Ok(())
}
pub fn dependencies_of<'a>(&'a self, target: &T) -> Result<DepGraphIterator<'a, T>,
SolventError>
{
let pos = match self._pos(target) {
None => return Err(SolventError::NoSuchNode),
Some(p) => p
};
Ok(DepGraphIterator {
depgraph: self,
target: pos,
satisfied: self.satisfied.clone(),
curpath: HashSet::new(),
halted: false,
})
}
}
pub struct DepGraphIterator<'a, T: Eq + 'a> {
depgraph: &'a DepGraph<T>,
target: usize,
satisfied: HashSet<usize>,
curpath: HashSet<usize>,
halted: bool,
}
impl<'a, T: Eq> DepGraphIterator<'a, T> {
fn get_next_dependency(&mut self, pos: usize) -> Result<usize, SolventError>
{
if self.curpath.contains(&pos) {
return Err(SolventError::CycleDetected);
}
self.curpath.insert(pos);
let deplist = match self.depgraph.dependencies.get(&pos) {
None => return Ok(pos),
Some(deplist) => deplist
};
for n in deplist.iter() {
if self.satisfied.contains(n) {
continue;
}
return self.get_next_dependency(*n);
}
Ok(pos)
}
}
impl<'a, T: Eq> Iterator for DepGraphIterator<'a, T> {
type Item = Result<&'a T, SolventError>;
fn next(&mut self) -> Option<Result<&'a T, SolventError>>
{
if self.halted {
return None;
}
let npos = self.target;
if self.satisfied.contains(&npos) {
self.halted = true;
return None;
}
self.curpath.clear();
let next = match self.get_next_dependency(npos) {
Ok(d) => d,
Err(e) => {
self.halted = true;
return Some(Err(e));
}
};
self.satisfied.insert(next);
Some(Ok(&self.depgraph.nodes[next]))
}
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use super::DepGraph;
use super::SolventError;
#[test]
fn solvent_test_branching() {
let mut depgraph: DepGraph<&str> = DepGraph::new();
depgraph.register_nodes(vec!["a","b","c","d","e","f","g","h","i","j","k","l","m","n"]);
depgraph.register_dependencies("a", vec!["b","c","d"]);
depgraph.register_dependency("b","d");
depgraph.register_dependencies("c", vec!["e","m","g"]);
depgraph.register_dependency("e","f");
depgraph.register_dependency("g","h");
depgraph.register_dependency("h","i");
depgraph.register_dependencies("i", vec!["j","k"]);
depgraph.register_dependencies("k", vec!["l","m"]);
depgraph.register_dependency("m","n");
let mut results: Vec<&str> = Vec::new();
for node in depgraph.dependencies_of(&"a").unwrap() {
assert!(results.len() < 30);
let n = match node {
Err(e) => panic!("Solvent error detected: {:?}", e),
Ok(n) => n,
};
let pos = depgraph._pos(&n).unwrap();
let deps: Option<&HashSet<usize>> = depgraph.dependencies.get(&pos);
if deps.is_some() {
for dep in deps.unwrap().iter() {
assert!( results.contains(&depgraph.nodes[*dep]) );
}
}
results.push(n.clone());
}
assert!(results.len() == 14);
for result in results.iter() {
let mut count: usize = 0;
for result2 in results.iter() {
if result == result2 { count = count + 1; }
}
assert!(count == 1);
}
}
#[test]
fn solvent_test_updating_dependencies() {
let mut depgraph: DepGraph<&str> = DepGraph::new();
depgraph.register_dependencies("a",vec!["b","c"]);
depgraph.register_dependency("a","d");
assert!(depgraph.dependencies.get(&0).unwrap().contains(&1));
assert!(depgraph.dependencies.get(&0).unwrap().contains(&2));
assert!(depgraph.dependencies.get(&0).unwrap().contains(&3));
}
#[test]
fn solvent_test_circular() {
let mut depgraph: DepGraph<&str> = DepGraph::new();
depgraph.register_dependency("a","b");
depgraph.register_dependency("b","c");
depgraph.register_dependency("c","a");
for node in depgraph.dependencies_of(&"a").unwrap() {
assert!(node.is_err());
assert!(node.unwrap_err() == SolventError::CycleDetected);
}
}
#[test]
fn solvent_test_satisfied_stoppage() {
let mut depgraph: DepGraph<&str> = DepGraph::new();
depgraph.register_dependencies("superconn", vec![]);
depgraph.register_dependencies("owneruser", vec!["superconn"]);
depgraph.register_dependencies("appuser", vec!["superconn"]);
depgraph.register_dependencies("database", vec!["owneruser"]);
depgraph.register_dependencies("ownerconn", vec!["database","owneruser"]);
depgraph.register_dependencies("adminconn", vec!["database"]);
depgraph.register_dependencies("extensions", vec!["database","adminconn"]);
depgraph.register_dependencies("schema_table", vec!["database","ownerconn"]);
depgraph.register_dependencies("schemas", vec!["ownerconn","extensions","schema_table","appuser"]);
depgraph.register_dependencies("appconn", vec!["database","appuser","schemas"]);
depgraph.mark_as_satisfied(&["owneruser","appuser"]).unwrap();
assert_eq!( depgraph.satisfied.len() , 2 );
let mut results: Vec<&str> = Vec::new();
for node in depgraph.dependencies_of(&"appconn").unwrap() {
assert!(results.len() < 30);
match node {
Ok(n) => results.push(n),
Err(e) => panic!("Solvent error detected: {:?}",e),
};
}
assert!( !results.contains( &"appuser") );
assert!( !results.contains( &"owneruser") );
assert!( !results.contains( &"superconn") );
assert!(results.len() == 7);
for result in results.iter() {
let mut count: usize = 0;
for result2 in results.iter() {
if result == result2 { count = count + 1; }
}
assert!(count == 1);
}
}
}