#![crate_name = "solvent"]
#![crate_type = "lib"]
#![feature(phase)]
#[phase(plugin, link)]
extern crate log;
use std::collections::{HashMap,HashSet};
use std::collections::hash_map::{Occupied,Vacant};
use std::iter::{Iterator};
#[allow(unused_imports)]
use std::task;
#[deriving(Clone)]
pub struct DepGraph {
pub dependencies: HashMap<String,HashSet<String>>,
pub satisfied: HashSet<String>,
}
#[deriving(Copy,Show,PartialEq)]
pub enum SolventError {
CycleDetected,
}
pub struct DepGraphIterator<'a> {
depgraph: &'a DepGraph,
target: String,
satisfied: HashSet<String>,
curpath: HashSet<String>,
halted: bool,
}
impl DepGraph {
pub fn new() -> DepGraph
{
DepGraph {
dependencies: HashMap::new(),
satisfied: HashSet::new(),
}
}
pub fn register_dependency<'a>( &mut self,
node: &'a str,
depends_on: &'a str )
{
match self.dependencies.entry( String::from_str(node) ) {
Vacant(entry) => {
let mut deps = HashSet::with_capacity(1);
deps.insert( String::from_str(depends_on) );
entry.set( deps );
},
Occupied(mut entry) => {
(*entry.get_mut()).insert(String::from_str(depends_on));
},
}
}
pub fn register_dependencies<'a>( &mut self,
node: &'a str,
depends_on: &'a[&'a str] )
{
match self.dependencies.entry( String::from_str(node) ) {
Vacant(entry) => {
let mut deps = HashSet::with_capacity( depends_on.len() );
for s in depends_on.iter() {
deps.insert( String::from_str(*s) );
}
entry.set( deps );
},
Occupied(mut entry) => {
for s in depends_on.iter() {
(*entry.get_mut()).insert( String::from_str(*s) );
}
},
}
}
pub fn mark_as_satisfied<'a>( &mut self,
nodes: &'a[&'a str] )
{
for node in nodes.iter() {
self.satisfied.insert(String::from_str(*node));
}
}
pub fn dependencies_of<'a>(&'a self, target: &str) -> DepGraphIterator<'a>
{
DepGraphIterator {
depgraph: self,
target: target.into_string(),
satisfied: self.satisfied.clone(),
curpath: HashSet::new(),
halted: false,
}
}
}
impl<'a> DepGraphIterator<'a> {
fn get_next_dependency(&mut self, node: &String) -> Result<String,SolventError>
{
if self.curpath.contains(node) {
return Err(SolventError::CycleDetected);
}
self.curpath.insert(node.clone());
let deplist = match self.depgraph.dependencies.get(node) {
None => return Ok(node.clone()),
Some(deplist) => deplist.clone() };
for n in deplist.iter() {
if self.satisfied.contains(n) {
continue;
}
return self.get_next_dependency(n);
}
Ok(node.clone())
}
}
impl<'a> Iterator< Result<String,SolventError> > for DepGraphIterator<'a> {
fn next(&mut self) -> Option< Result<String,SolventError> >
{
if self.halted {
return None;
}
let node = self.target.clone();
if self.satisfied.contains(&node) {
return None;
}
self.curpath.clear();
let next = match self.get_next_dependency(&node) {
Ok(d) => d,
Err(e) => {
self.halted = true;
return Some(Err(e));
}
};
self.satisfied.insert(next.clone());
Some(Ok(next))
}
}
#[test]
fn solvent_test_branching() {
let mut depgraph: DepGraph = DepGraph::new();
depgraph.register_dependencies("a",&["b","c","d"]);
depgraph.register_dependency("b","d");
depgraph.register_dependencies("c",&["e","m","g"]);
depgraph.register_dependency("e","f");
depgraph.register_dependency("g","h");
depgraph.register_dependency("h","i");
depgraph.register_dependencies("i",&["j","k"]);
depgraph.register_dependencies("k",&["l","m"]);
depgraph.register_dependency("m","n");
let mut results: Vec<String> = Vec::new();
for node in depgraph.dependencies_of("a") {
assert!(results.len() < 30);
let n = node.unwrap();
let deps: Option<&HashSet<String>> = depgraph.dependencies.get(&n);
if deps.is_some() {
for dep in deps.unwrap().iter() {
assert!( results.contains(dep) );
}
}
results.push(n.clone());
}
assert!(results.len() == 14);
for result in results.iter() {
let mut count = 0u;
for result2 in results.iter() {
if result == result2 { count = count + 1; }
}
assert!(count == 1);
}
}
#[test]
fn solvent_test_updating_dependencies() {
let mut depgraph: DepGraph = DepGraph::new();
depgraph.register_dependencies("a",&["b","c"]);
depgraph.register_dependency("a","d");
assert!(depgraph.dependencies.get("a").unwrap().contains("b"));
assert!(depgraph.dependencies.get("a").unwrap().contains("c"));
assert!(depgraph.dependencies.get("a").unwrap().contains("d"));
}
#[test]
fn solvent_test_circular() {
let mut depgraph: DepGraph = DepGraph::new();
depgraph.register_dependency("a","b");
depgraph.register_dependency("b","c");
depgraph.register_dependency("c","a");
for node in depgraph.dependencies_of("a") {
assert!(node.is_err());
assert!(node.unwrap_err() == SolventError::CycleDetected);
}
}
#[test]
fn solvent_test_satisfied_stoppage() {
let mut depgraph: DepGraph = DepGraph::new();
depgraph.register_dependencies("superconn", &[]);
depgraph.register_dependencies("owneruser", &["superconn"]);
depgraph.register_dependencies("appuser", &["superconn"]);
depgraph.register_dependencies("database", &["owneruser"]);
depgraph.register_dependencies("ownerconn", &["database","owneruser"]);
depgraph.register_dependencies("adminconn", &["database"]);
depgraph.register_dependencies("extensions", &["database","adminconn"]);
depgraph.register_dependencies("schema_table", &["database","ownerconn"]);
depgraph.register_dependencies("schemas", &["ownerconn","extensions","schema_table","appuser"]);
depgraph.register_dependencies("appconn", &["database","appuser","schemas"]);
depgraph.mark_as_satisfied(&["owneruser","appuser"]);
let mut results: Vec<String> = Vec::new();
for node in depgraph.dependencies_of("appconn") {
assert!(results.len() < 30);
results.push(node.unwrap());
}
assert!( !results.contains(&String::from_str("appuser")) );
assert!( !results.contains(&String::from_str("owneruser")) );
assert!( !results.contains(&String::from_str("superconn")) );
assert!(results.len() == 7);
for result in results.iter() {
let mut count = 0u;
for result2 in results.iter() {
if result == result2 { count = count + 1; }
}
assert!(count == 1);
}
}