<commit_before>// Dependency Graph Library in Rust

//! dglr is a Dependency Graph Library in Rust.
//! In short, you register elements and their dependencies, and then
//! ask for the dependencies of any element in an order that satisfies.
//!
//! Elements are simply &str strings.  You register them with
//! register_dependency() or register_dependencies().
//!
//! Dependenies are (currently) just a list of other elements that
//! the element in question depends on.  [In the future, we may add
//! boolean logic, but currently they are all AND-ed together].
//!
//! Then iterate over .iter() to get the ordered dependencies, and
//! the library does the magic and returns an order which will work.
//! It is possible that other orders also work, but the library's
//! algorithm is deterministic, so you'll always get the same
//! particular one.

#![crate_name = "dglr"]
#![crate_type = "lib"]

// Required for log and rustdoc:
#![feature(phase)]
#[phase(plugin, link)]

extern crate log;

use std::collections::hashmap::{HashMap,HashSet};
use std::iter::{Iterator};
#[allow(unused_imports)]
use std::task;

/// This is the dependency graph.  You can create it yourself, or
/// use the convenience methods of new(), register_dependency() and
/// register_dependencies() to build one up (that must be mutable).
pub struct DepGraph {
    /// List of dependencies.  Key is the element, values are the
    /// other elements that the key element depends upon.
    pub dependencies: HashMap<String,Vec<String>>,

    // (private) target we are trying to satisfy
    target: Option<String>,

    // (private) elements already satisfied
    satisfied: HashSet<String>,

    // (private) current path, for cycle detection
    curpath: HashSet<String>,
}

pub struct DepGraphIterator<'a> {
    depgraph: &'a mut DepGraph
}
pub struct DepGraphSatisfyingIterator<'a> {
    depgraph: &'a mut DepGraph
}

impl DepGraph {

    /// Create an empty DepGraph.
    pub fn new() -> DepGraph
    {
        DepGraph {
            dependencies: HashMap::new(),
            target: None,
            curpath: HashSet::new(),
            satisfied: HashSet::new(),
        }
    }

    /// Add a dependency to a DepGraph.  The thing does not need
    /// to pre-exist, nor do the dependency elements.  But if the
    /// thing does pre-exist, the depends_on will be added to its
    /// existing dependency list.
    pub fn register_dependency<'a>( &mut self,
                                thing: &'a str,
                                depends_on: &'a str )
    {
        self.dependencies.insert_or_update_with(
            String::from_str(thing),
            vec![String::from_str(depends_on)],
            |_,v| { v.push(String::from_str(depends_on)); }
            );
    }

    /// Add multiple dependencies of one thing to a DepGraph.  The
    /// thing does not need to pre-exist, nor do the dependency elements.
    /// But if the thing does pre-exist, the depends_on will be added
    /// to its existing dependency list.
    pub fn register_dependencies<'a>( &mut self,
                                  thing: &'a str,
                                  depends_on: &'a[&'a str] )
    {
        let newvec: Vec<String> = depends_on.iter().map(
            |s| String::from_str(*s)).collect();

        self.dependencies.insert_or_update_with(
            String::from_str(thing),
            newvec.clone(),
            |_,v| { v.push_all(newvec.as_slice()); }
            );
    }

    pub fn set_target<'a>( &mut self, target: &'a str )
    {
        self.target = Some(String::from_str(target));
    }

    pub fn mark_as_satisfied<'a>( &mut self,
                                   things: &'a[&'a str] )
    {
        for thing in things.iter() {
            self.satisfied.insert(String::from_str(*thing));
        }
    }

    fn get_next_dependency(&mut self, thing: &String) -> String
    {
        if self.curpath.contains(thing) {
            fail!("Circular dependency graph at {}",thing);
        }
        self.curpath.insert(thing.clone());

        let deplist = match self.dependencies.find(thing) {
            None => return thing.clone(),
            Some(deplist) => deplist.clone() // ouch
        };

        for n in deplist.iter() {
            if self.satisfied.contains(n) {
                continue;
            }
            return self.get_next_dependency(n);
        }
        // things dependencies are satisfied
        thing.clone()
    }

    pub fn iter<'a>(&'a mut self) -> DepGraphIterator<'a>
    {
        DepGraphIterator {
            depgraph: self
        }
    }

    pub fn satisfying_iter<'a>(&'a mut self) -> DepGraphSatisfyingIterator<'a>
    {
        DepGraphSatisfyingIterator {
            depgraph: self
        }
    }
}

impl<'a> Iterator<String> for DepGraphIterator<'a> {
    fn next(&mut self) -> Option<String>
    {
        let thing = match self.depgraph.target {
            None => return None,
            Some(ref thing) => thing.clone()
        };
        if self.depgraph.satisfied.contains(&thing) {
            return None;
        }
        self.depgraph.curpath.clear();
        Some(self.depgraph.get_next_dependency(&thing))
    }
}

impl<'a> Iterator<String> for DepGraphSatisfyingIterator<'a> {
    fn next(&mut self) -> Option<String>
    {
        let thing = match self.depgraph.target {
            None => return None,
            Some(ref thing) => thing.clone()
        };
        if self.depgraph.satisfied.contains(&thing) {
            return None;
        }
        self.depgraph.curpath.clear();
        let next = self.depgraph.get_next_dependency(&thing);
        self.depgraph.mark_as_satisfied([next.as_slice()]);
        Some(next)
    }
}

#[test]
fn dglr_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");

    depgraph.set_target("a");

    let mut results: Vec<String> = Vec::new();

    loop {
        // detect infinite looping bugs
        assert!(results.len() < 30);

        let thing = match depgraph.iter().next() {
            Some(x) => x,
            None => break,
        };
        depgraph.mark_as_satisfied([thing.as_slice()]);
        results.push(thing);
    }

    assert!( results == vec![String::from_str("d"),
                             String::from_str("b"),
                             String::from_str("f"),
                             String::from_str("e"),
                             String::from_str("n"),
                             String::from_str("m"),
                             String::from_str("j"),
                             String::from_str("l"),
                             String::from_str("k"),
                             String::from_str("i"),
                             String::from_str("h"),
                             String::from_str("g"),
                             String::from_str("c"),
                             String::from_str("a")] );
    //info!("Deps of a = {}",results);
}

#[test]
fn dglr_test_satisfying() {
    let mut depgraph: DepGraph = DepGraph::new();

    depgraph.register_dependencies("a",&["b","c","d"]);
    depgraph.register_dependency("b","d");
    depgraph.register_dependencies("c",&["e"]);

    depgraph.set_target("a");

    let mut results: Vec<String> = Vec::new();

    for thing in depgraph.satisfying_iter() {
        // detect infinite looping bugs
        assert!(results.len() < 30);
        results.push(thing);
    }

    assert!( results == vec![String::from_str("d"),
                             String::from_str("b"),
                             String::from_str("e"),
                             String::from_str("c"),
                             String::from_str("a")] );
    //info!("Deps of a = {}",results);
}

#[test]
fn dglr_test_circular() {

    let task_result = task::try(proc() {
        let mut depgraph: DepGraph = DepGraph::new();
        depgraph.register_dependency("a","b");
        depgraph.register_dependency("b","c");
        depgraph.register_dependency("c","a");
        depgraph.set_target("a");

        let mut results: Vec<String> = Vec::new();

        loop {
            // Detect infinite looping bugs by
            // breaking out successfully (successful
            // proc() means failed test!)
            if results.len() >= 30 { break; }

            let thing = match depgraph.iter().next() {
                Some(x) => x,
                None => break,
            };
            depgraph.mark_as_satisfied([thing.as_slice()]);
            results.push(thing);
        }
    });
    match task_result {
        Ok(_) => fail!("Should have failed at the circular dependency!"),
        Err(_) => () //info!("Successfully detected the circular dependency (ignore task failure)"),
    };
}

#[test]
fn dglr_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.set_target("appconn");
    depgraph.mark_as_satisfied(["owneruser","appuser"]);

    let mut results: Vec<String> = Vec::new();

    loop {
        assert!(results.len() < 30);

        let thing = match depgraph.iter().next() {
            Some(x) => x,
            None => break,
        };
        depgraph.mark_as_satisfied([thing.as_slice()]);
        results.push(thing);
    }
    assert!( !results.contains(&String::from_str("superconn")) );
}
<commit_msg>upstream rust: Updated deprecated functionality<commit_after>// Dependency Graph Library in Rust

//! dglr is a Dependency Graph Library in Rust.
//! In short, you register elements and their dependencies, and then
//! ask for the dependencies of any element in an order that satisfies.
//!
//! Elements are simply &str strings.  You register them with
//! register_dependency() or register_dependencies().
//!
//! Dependenies are (currently) just a list of other elements that
//! the element in question depends on.  [In the future, we may add
//! boolean logic, but currently they are all AND-ed together].
//!
//! Then iterate over .iter() to get the ordered dependencies, and
//! the library does the magic and returns an order which will work.
//! It is possible that other orders also work, but the library's
//! algorithm is deterministic, so you'll always get the same
//! particular one.

#![crate_name = "dglr"]
#![crate_type = "lib"]

// Required for log and rustdoc:
#![feature(phase)]
#[phase(plugin, link)]

extern crate log;

use std::collections::hashmap::{HashMap,HashSet,Occupied,Vacant};
use std::iter::{Iterator};
#[allow(unused_imports)]
use std::task;

/// This is the dependency graph.  You can create it yourself, or
/// use the convenience methods of new(), register_dependency() and
/// register_dependencies() to build one up (that must be mutable).
pub struct DepGraph {
    /// List of dependencies.  Key is the element, values are the
    /// other elements that the key element depends upon.
    pub dependencies: HashMap<String,Vec<String>>,

    // (private) target we are trying to satisfy
    target: Option<String>,

    // (private) elements already satisfied
    satisfied: HashSet<String>,

    // (private) current path, for cycle detection
    curpath: HashSet<String>,
}

pub struct DepGraphIterator<'a> {
    depgraph: &'a mut DepGraph
}
pub struct DepGraphSatisfyingIterator<'a> {
    depgraph: &'a mut DepGraph
}

impl DepGraph {

    /// Create an empty DepGraph.
    pub fn new() -> DepGraph
    {
        DepGraph {
            dependencies: HashMap::new(),
            target: None,
            curpath: HashSet::new(),
            satisfied: HashSet::new(),
        }
    }

    /// Add a dependency to a DepGraph.  The thing does not need
    /// to pre-exist, nor do the dependency elements.  But if the
    /// thing does pre-exist, the depends_on will be added to its
    /// existing dependency list.
    pub fn register_dependency<'a>( &mut self,
                                thing: &'a str,
                                depends_on: &'a str )
    {
        match self.dependencies.entry( String::from_str(thing) ) {
            Vacant(entry) => { entry.set( vec![String::from_str(depends_on)] ); },
            Occupied(mut entry) => { (*entry.get_mut()).push(String::from_str(depends_on)); },
        }
    }

    /// Add multiple dependencies of one thing to a DepGraph.  The
    /// thing does not need to pre-exist, nor do the dependency elements.
    /// But if the thing does pre-exist, the depends_on will be added
    /// to its existing dependency list.
    pub fn register_dependencies<'a>( &mut self,
                                  thing: &'a str,
                                  depends_on: &'a[&'a str] )
    {
        let newvec: Vec<String> = depends_on.iter().map(
            |s| String::from_str(*s)).collect();

        match self.dependencies.entry( String::from_str(thing) ) {
            Vacant(entry) => { entry.set( newvec.clone() ); },
            Occupied(mut entry) => { (*entry.get_mut()).push_all(newvec.as_slice()); },
        }
    }

    pub fn set_target<'a>( &mut self, target: &'a str )
    {
        self.target = Some(String::from_str(target));
    }

    pub fn mark_as_satisfied<'a>( &mut self,
                                   things: &'a[&'a str] )
    {
        for thing in things.iter() {
            self.satisfied.insert(String::from_str(*thing));
        }
    }

    fn get_next_dependency(&mut self, thing: &String) -> String
    {
        if self.curpath.contains(thing) {
            fail!("Circular dependency graph at {}",thing);
        }
        self.curpath.insert(thing.clone());

        let deplist = match self.dependencies.find(thing) {
            None => return thing.clone(),
            Some(deplist) => deplist.clone() // ouch
        };

        for n in deplist.iter() {
            if self.satisfied.contains(n) {
                continue;
            }
            return self.get_next_dependency(n);
        }
        // things dependencies are satisfied
        thing.clone()
    }

    pub fn iter<'a>(&'a mut self) -> DepGraphIterator<'a>
    {
        DepGraphIterator {
            depgraph: self
        }
    }

    pub fn satisfying_iter<'a>(&'a mut self) -> DepGraphSatisfyingIterator<'a>
    {
        DepGraphSatisfyingIterator {
            depgraph: self
        }
    }
}

impl<'a> Iterator<String> for DepGraphIterator<'a> {
    fn next(&mut self) -> Option<String>
    {
        let thing = match self.depgraph.target {
            None => return None,
            Some(ref thing) => thing.clone()
        };
        if self.depgraph.satisfied.contains(&thing) {
            return None;
        }
        self.depgraph.curpath.clear();
        Some(self.depgraph.get_next_dependency(&thing))
    }
}

impl<'a> Iterator<String> for DepGraphSatisfyingIterator<'a> {
    fn next(&mut self) -> Option<String>
    {
        let thing = match self.depgraph.target {
            None => return None,
            Some(ref thing) => thing.clone()
        };
        if self.depgraph.satisfied.contains(&thing) {
            return None;
        }
        self.depgraph.curpath.clear();
        let next = self.depgraph.get_next_dependency(&thing);
        self.depgraph.mark_as_satisfied([next.as_slice()]);
        Some(next)
    }
}

#[test]
fn dglr_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");

    depgraph.set_target("a");

    let mut results: Vec<String> = Vec::new();

    loop {
        // detect infinite looping bugs
        assert!(results.len() < 30);

        let thing = match depgraph.iter().next() {
            Some(x) => x,
            None => break,
        };
        depgraph.mark_as_satisfied([thing.as_slice()]);
        results.push(thing);
    }

    assert!( results == vec![String::from_str("d"),
                             String::from_str("b"),
                             String::from_str("f"),
                             String::from_str("e"),
                             String::from_str("n"),
                             String::from_str("m"),
                             String::from_str("j"),
                             String::from_str("l"),
                             String::from_str("k"),
                             String::from_str("i"),
                             String::from_str("h"),
                             String::from_str("g"),
                             String::from_str("c"),
                             String::from_str("a")] );
    //info!("Deps of a = {}",results);
}

#[test]
fn dglr_test_satisfying() {
    let mut depgraph: DepGraph = DepGraph::new();

    depgraph.register_dependencies("a",&["b","c","d"]);
    depgraph.register_dependency("b","d");
    depgraph.register_dependencies("c",&["e"]);

    depgraph.set_target("a");

    let mut results: Vec<String> = Vec::new();

    for thing in depgraph.satisfying_iter() {
        // detect infinite looping bugs
        assert!(results.len() < 30);
        results.push(thing);
    }

    assert!( results == vec![String::from_str("d"),
                             String::from_str("b"),
                             String::from_str("e"),
                             String::from_str("c"),
                             String::from_str("a")] );
    //info!("Deps of a = {}",results);
}

#[test]
fn dglr_test_circular() {

    let task_result = task::try(proc() {
        let mut depgraph: DepGraph = DepGraph::new();
        depgraph.register_dependency("a","b");
        depgraph.register_dependency("b","c");
        depgraph.register_dependency("c","a");
        depgraph.set_target("a");

        let mut results: Vec<String> = Vec::new();

        loop {
            // Detect infinite looping bugs by
            // breaking out successfully (successful
            // proc() means failed test!)
            if results.len() >= 30 { break; }

            let thing = match depgraph.iter().next() {
                Some(x) => x,
                None => break,
            };
            depgraph.mark_as_satisfied([thing.as_slice()]);
            results.push(thing);
        }
    });
    match task_result {
        Ok(_) => fail!("Should have failed at the circular dependency!"),
        Err(_) => () //info!("Successfully detected the circular dependency (ignore task failure)"),
    };
}

#[test]
fn dglr_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.set_target("appconn");
    depgraph.mark_as_satisfied(["owneruser","appuser"]);

    let mut results: Vec<String> = Vec::new();

    loop {
        assert!(results.len() < 30);

        let thing = match depgraph.iter().next() {
            Some(x) => x,
            None => break,
        };
        depgraph.mark_as_satisfied([thing.as_slice()]);
        results.push(thing);
    }
    assert!( !results.contains(&String::from_str("superconn")) );
}
<|endoftext|>


<commit_before>// Copyright 2014 Nick Fitzgerald
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Evaluating values.

use std::fmt::{format};
use context::{Context};
use value::{SchemeResult, Value};

fn is_auto_quoting(val: &Value) -> bool {
    match *val {
        Value::EmptyList    => false,
        Value::Pair(_)      => false,
        Value::Symbol(_)    => false,
        _                   => true,
    }
}

/// Evaluate the given value.
pub fn evaluate(ctx: &mut Context, val: Value) -> SchemeResult {
    if val.is_atom() {
        if is_auto_quoting(&val) {
            return Ok(val);
        }

        if let Value::Symbol(sym) = val {
            return ctx.env().lookup(sym.deref());
        }

        return Err(format_args!(format, "Cannot evaluate: {}", val));
    }

    let pair = val.to_pair()
        .expect("If a value is not an atom, then it must be a pair.");

    let quote = ctx.quote_symbol();
    let if_symbol = ctx.if_symbol();
    let begin = ctx.begin_symbol();
    let define = ctx.define_symbol();
    let set_bang = ctx.set_bang_symbol();

    match pair.car() {
        // Quoted forms.
        v if v == quote => {
            if let Some(Value::EmptyList) = val.cdr().unwrap().cdr() {
                return Ok(val.cdr().unwrap().car().unwrap());
            }

            return Err("Wrong number of parts in quoted form".to_string());
        },

        // If expressions.
        v if v == if_symbol => {
            if let Ok(4) = val.len() {
                let condition_form = try!(pair.cadr());
                let condition_val = try!(evaluate(ctx, condition_form));

                if condition_val == Value::new_boolean(false) {
                    let alternative_form = try!(pair.cadddr());
                    return evaluate(ctx, alternative_form);
                }

                let consequent_form = try!(pair.caddr());
                return evaluate(ctx, consequent_form);
            }

            return Err("Improperly formed if expression".to_string());
        },

        // `(begin ...)` sequences.
        v if v == begin => {
            return evaluate_sequence(ctx, pair.cdr());
        },

        // Definitions. These are only supposed to be allowed at the top level
        // and at the beginning of a body, but we are punting on that
        // restriction for now.
        v if v == define => {
            if let Ok(3) = val.len() {
                let sym = try!(pair.cadr());

                if let Some(str) = sym.to_symbol() {
                    let def_value_form = try!(pair.caddr());
                    let def_value = try!(evaluate(ctx, def_value_form));
                    ctx.env().define(str.deref().clone(), def_value);
                    return Ok(ctx.unspecified_symbol());
                }

                return Err("Can only define symbols".to_string());
            }

            return Err("Improperly formed definition".to_string());
        },

        // `set!` assignment.
        v if v == set_bang => {
            if let Ok(3) = val.len() {
                let sym = try!(pair.cadr());

                if let Some(str) = sym.to_symbol() {
                    let new_value_form = try!(pair.caddr());
                    let new_value = try!(evaluate(ctx, new_value_form));
                    try!(ctx.env().update(str.deref().clone(), new_value));
                    return Ok(ctx.unspecified_symbol());
                }

                return Err("Can only set! symbols".to_string());
            }

            return Err("Improperly formed set! expression".to_string());
        },

        _                  => {
            return Err(format_args!(format, "Cannot evaluate: {}", val));
        },
    };
}

/// Evaluate each expression in the given cons list `exprs` and return the value
/// of the last expression.
fn evaluate_sequence(ctx: &mut Context, exprs: Value) -> SchemeResult {
    let mut e = exprs;
    loop {
        match e {
            Value::EmptyList  => return Ok(Value::EmptyList),
            Value::Pair(pair) => {
                let v = try!(evaluate(ctx, pair.car()));
                if pair.cdr() == Value::EmptyList {
                    return Ok(v);
                } else {
                    e = pair.cdr();
                }
            },
            _                 => {
                return Err("Bad sequence of expressions".to_string());
            },
        }
    }
}

#[test]
fn test_eval_integer() {
    let mut ctx = Context::new();
    assert_eq!(evaluate(&mut ctx, Value::new_integer(42)),
               Ok(Value::new_integer(42)));
}

#[test]
fn test_eval_boolean() {
    let mut ctx = Context::new();
    assert_eq!(evaluate(&mut ctx, Value::new_boolean(true)),
               Ok(Value::new_boolean(true)));
}

#[test]
fn test_eval_quoted() {
    use value::list;

    let mut ctx = Context::new();
    let val = Value::new_integer(5);
    let mut items = [
        ctx.quote_symbol(),
        val
    ];
    let quoted = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, quoted),
               Ok(val));
}

#[test]
fn test_eval_if_consequent() {
    use value::list;

    let mut ctx = Context::new();
    let mut items = [
        ctx.if_symbol(),
        Value::new_boolean(true),
        Value::new_integer(1),
        Value::new_integer(2)
    ];
    let if_form = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, if_form),
               Ok(Value::new_integer(1)));
}

#[test]
fn test_eval_if_alternative() {
    use value::list;

    let mut ctx = Context::new();
    let mut items = [
        ctx.if_symbol(),
        Value::new_boolean(false),
        Value::new_integer(1),
        Value::new_integer(2)
    ];
    let if_form = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, if_form),
               Ok(Value::new_integer(2)));
}

#[test]
fn test_eval_begin() {
    use value::list;

    let mut ctx = Context::new();
    let mut items = [
        ctx.begin_symbol(),
        Value::new_integer(1),
        Value::new_integer(2)
    ];
    let begin_form = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, begin_form),
               Ok(Value::new_integer(2)));
}<commit_msg>Add a test for defining/setting/getting variables.<commit_after>// Copyright 2014 Nick Fitzgerald
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Evaluating values.

use std::fmt::{format};
use context::{Context};
use value::{SchemeResult, Value};

fn is_auto_quoting(val: &Value) -> bool {
    match *val {
        Value::EmptyList    => false,
        Value::Pair(_)      => false,
        Value::Symbol(_)    => false,
        _                   => true,
    }
}

/// Evaluate the given value.
pub fn evaluate(ctx: &mut Context, val: Value) -> SchemeResult {
    if val.is_atom() {
        if is_auto_quoting(&val) {
            return Ok(val);
        }

        if let Value::Symbol(sym) = val {
            return ctx.env().lookup(sym.deref());
        }

        return Err(format_args!(format, "Cannot evaluate: {}", val));
    }

    let pair = val.to_pair()
        .expect("If a value is not an atom, then it must be a pair.");

    let quote = ctx.quote_symbol();
    let if_symbol = ctx.if_symbol();
    let begin = ctx.begin_symbol();
    let define = ctx.define_symbol();
    let set_bang = ctx.set_bang_symbol();

    match pair.car() {
        // Quoted forms.
        v if v == quote => {
            if let Some(Value::EmptyList) = val.cdr().unwrap().cdr() {
                return Ok(val.cdr().unwrap().car().unwrap());
            }

            return Err("Wrong number of parts in quoted form".to_string());
        },

        // If expressions.
        v if v == if_symbol => {
            if let Ok(4) = val.len() {
                let condition_form = try!(pair.cadr());
                let condition_val = try!(evaluate(ctx, condition_form));

                if condition_val == Value::new_boolean(false) {
                    let alternative_form = try!(pair.cadddr());
                    return evaluate(ctx, alternative_form);
                }

                let consequent_form = try!(pair.caddr());
                return evaluate(ctx, consequent_form);
            }

            return Err("Improperly formed if expression".to_string());
        },

        // `(begin ...)` sequences.
        v if v == begin => {
            return evaluate_sequence(ctx, pair.cdr());
        },

        // Definitions. These are only supposed to be allowed at the top level
        // and at the beginning of a body, but we are punting on that
        // restriction for now.
        v if v == define => {
            if let Ok(3) = val.len() {
                let sym = try!(pair.cadr());

                if let Some(str) = sym.to_symbol() {
                    let def_value_form = try!(pair.caddr());
                    let def_value = try!(evaluate(ctx, def_value_form));
                    ctx.env().define(str.deref().clone(), def_value);
                    return Ok(ctx.unspecified_symbol());
                }

                return Err("Can only define symbols".to_string());
            }

            return Err("Improperly formed definition".to_string());
        },

        // `set!` assignment.
        v if v == set_bang => {
            if let Ok(3) = val.len() {
                let sym = try!(pair.cadr());

                if let Some(str) = sym.to_symbol() {
                    let new_value_form = try!(pair.caddr());
                    let new_value = try!(evaluate(ctx, new_value_form));
                    try!(ctx.env().update(str.deref().clone(), new_value));
                    return Ok(ctx.unspecified_symbol());
                }

                return Err("Can only set! symbols".to_string());
            }

            return Err("Improperly formed set! expression".to_string());
        },

        _                  => {
            return Err(format_args!(format, "Cannot evaluate: {}", val));
        },
    };
}

/// Evaluate each expression in the given cons list `exprs` and return the value
/// of the last expression.
fn evaluate_sequence(ctx: &mut Context, exprs: Value) -> SchemeResult {
    let mut e = exprs;
    loop {
        match e {
            Value::EmptyList  => return Ok(Value::EmptyList),
            Value::Pair(pair) => {
                let v = try!(evaluate(ctx, pair.car()));
                if pair.cdr() == Value::EmptyList {
                    return Ok(v);
                } else {
                    e = pair.cdr();
                }
            },
            _                 => {
                return Err("Bad sequence of expressions".to_string());
            },
        }
    }
}

#[test]
fn test_eval_integer() {
    let mut ctx = Context::new();
    assert_eq!(evaluate(&mut ctx, Value::new_integer(42)),
               Ok(Value::new_integer(42)));
}

#[test]
fn test_eval_boolean() {
    let mut ctx = Context::new();
    assert_eq!(evaluate(&mut ctx, Value::new_boolean(true)),
               Ok(Value::new_boolean(true)));
}

#[test]
fn test_eval_quoted() {
    use value::list;

    let mut ctx = Context::new();
    let val = Value::new_integer(5);
    let mut items = [
        ctx.quote_symbol(),
        val
    ];
    let quoted = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, quoted),
               Ok(val));
}

#[test]
fn test_eval_if_consequent() {
    use value::list;

    let mut ctx = Context::new();
    let mut items = [
        ctx.if_symbol(),
        Value::new_boolean(true),
        Value::new_integer(1),
        Value::new_integer(2)
    ];
    let if_form = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, if_form),
               Ok(Value::new_integer(1)));
}

#[test]
fn test_eval_if_alternative() {
    use value::list;

    let mut ctx = Context::new();
    let mut items = [
        ctx.if_symbol(),
        Value::new_boolean(false),
        Value::new_integer(1),
        Value::new_integer(2)
    ];
    let if_form = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, if_form),
               Ok(Value::new_integer(2)));
}

#[test]
fn test_eval_begin() {
    use value::list;

    let mut ctx = Context::new();
    let mut items = [
        ctx.begin_symbol(),
        Value::new_integer(1),
        Value::new_integer(2)
    ];
    let begin_form = list(&mut ctx, &mut items);
    assert_eq!(evaluate(&mut ctx, begin_form),
               Ok(Value::new_integer(2)));
}

#[test]
fn test_eval_variables() {
    use value::list;

    let mut ctx = Context::new();

    let define_symbol = ctx.define_symbol();
    let set_bang_symbol = ctx.set_bang_symbol();
    let foo_symbol = ctx.get_or_create_symbol("foo".to_string());

    let mut def_items = [
        define_symbol,
        foo_symbol,
        Value::new_integer(2)
    ];
    let def_form = list(&mut ctx, &mut def_items);
    evaluate(&mut ctx, def_form).ok().expect("Should be able to define");

    let def_val = evaluate(&mut ctx, foo_symbol).ok()
        .expect("Should be able to get a defined symbol's value");
    assert_eq!(def_val, Value::new_integer(2));

    let mut set_items = [
        set_bang_symbol,
        foo_symbol,
        Value::new_integer(1)
    ];
    let set_form = list(&mut ctx, &mut set_items);
    evaluate(&mut ctx, set_form).ok().expect("Should be able to define");

    let set_val = evaluate(&mut ctx, foo_symbol).ok()
        .expect("Should be able to get a defined symbol's value");
    assert_eq!(set_val, Value::new_integer(1));
}<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::cmp;

pub struct Cursor {
    pub x: i32,
    pub y: i32,
}

pub struct File {
    pub name: String,
    pub lines: Vec<String>,
    file_cursor: Cursor,
    y_offset: i32,
    last_dim: (i32, i32),
    misc: String,
}

impl File {
    pub fn debug(&self, dim: (i32, i32)) -> String {
        format!("FC ({}, {}) O ({}) C ({}, {}) {}",
            self.file_cursor.x, self.file_cursor.y, self.y_offset,
            self.cursor(dim).x, self.cursor(dim).y, self.misc
        )
    }

    pub fn open(path: &str) -> File {
        let f = fs::File::open(path).expect("Could not open file");
        let f = io::BufReader::new(f);

        File {
            name: String::from(Path::new(path).file_name().unwrap().to_string_lossy()),
            lines: f.lines().map(|r| r.unwrap()).collect(),
            file_cursor: Cursor { x: 1, y: 1 },
            y_offset: 0,
            last_dim: (0, 0),
            misc: String::from(""),
        }
    }

    pub fn cursor(&self, dim: (i32, i32)) -> Cursor {
        Cursor {
            x: (self.file_cursor.x - 1) % dim.0 + 1,
            y: self.file_cursor.y + (self.file_cursor.x - 1) / dim.0 + self.y_offset,
        }
    }

    pub fn wrapped_lines(&self, dim: (i32, i32)) -> Vec<(Option<u16>, String)> {
        let mut result = vec![];
        let (width, _) = dim;
        for (line_number, raw_line) in self.lines.iter().enumerate() {
            let mut line_start = 0;
            let mut line_end = cmp::min(raw_line.len(), width as usize);
            result.push((Some(line_number as u16),
                         String::from(&raw_line[line_start..line_end])));
            while line_end < raw_line.len() {
                line_start = line_end;
                line_end += cmp::min(raw_line.len() - line_end, width as usize);
                result.push((None,
                             String::from(&raw_line[line_start..line_end])));
            }
        }
        result
    }

    fn current_line(&self) -> &String {
        &self.lines[self.file_cursor.y as usize - 1]
    }

    fn recompute_offsets(&mut self, dim: (i32, i32)) {
        if dim != self.last_dim {
            panic!("AAA I CAN'T DO THIS YET");
        }
    }

    pub fn move_cursor_left(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x > 1 {
            self.file_cursor.x -= 1;
        } else if self.file_cursor.y > 1 {
            self.file_cursor.x = dim.0;
            self.move_cursor_up(dim);
        }
    }

    pub fn move_cursor_right(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x <= self.current_line().len() as i32 {
            self.file_cursor.x += 1;
        } else if self.file_cursor.y < self.lines.len() as i32 {
            self.file_cursor.x = 1;
            self.move_cursor_down(dim);
        }
    }

    pub fn move_cursor_up(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x > dim.0 {
            self.file_cursor.x -= dim.0;
        } else if self.file_cursor.y > 1 {
            self.file_cursor.y -= 1;
            let extra_lines = self.current_line().len() as i32 / dim.0;
            self.y_offset -= self.current_line().len() as i32 / dim.0;
            self.file_cursor.x += extra_lines * dim.0;
        }
    }

    pub fn move_cursor_down(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x / dim.0 < self.current_line().len() as i32 / dim.0 {
            self.file_cursor.x += dim.0;
        } else if self.file_cursor.y < self.lines.len() as i32 {
            self.file_cursor.y += 1;
            while self.file_cursor.x > dim.0 {
                self.file_cursor.x -= dim.0;
                self.y_offset += 1;
            }
        }
    }

    pub fn insert(&mut self, dim: (i32, i32), c: char) {
        {
            let x = self.file_cursor.x - 1;
            let line = &mut self.lines[self.file_cursor.y as usize - 1];
            let pos = if x > line.len() as i32 {
                self.file_cursor.x = line.len() as i32 - 1;
                line.len()
            } else {
                x as usize
            };
            line.insert(pos, c);
        }
        self.move_cursor_right(dim);
    }
}
<commit_msg>Make cursor movement more predictable<commit_after>use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::cmp;

pub struct Cursor {
    pub x: i32,
    pub y: i32,
}

pub struct File {
    pub name: String,
    pub lines: Vec<String>,
    file_cursor: Cursor,
    y_offset: i32,
    last_dim: (i32, i32),
    misc: String,
}

impl File {
    pub fn debug(&self, dim: (i32, i32)) -> String {
        format!("FC ({}, {}) O ({}) C ({}, {}) {}",
            self.file_cursor.x, self.file_cursor.y, self.y_offset,
            self.cursor(dim).x, self.cursor(dim).y, self.misc
        )
    }

    pub fn open(path: &str) -> File {
        let f = fs::File::open(path).expect("Could not open file");
        let f = io::BufReader::new(f);

        File {
            name: String::from(Path::new(path).file_name().unwrap().to_string_lossy()),
            lines: f.lines().map(|r| r.unwrap()).collect(),
            file_cursor: Cursor { x: 1, y: 1 },
            y_offset: 0,
            last_dim: (0, 0),
            misc: String::from(""),
        }
    }

    pub fn cursor(&self, dim: (i32, i32)) -> Cursor {
        Cursor {
            x: (self.file_cursor.x - 1) % dim.0 + 1,
            y: self.file_cursor.y + (self.file_cursor.x - 1) / dim.0 + self.y_offset,
        }
    }

    pub fn wrapped_lines(&self, dim: (i32, i32)) -> Vec<(Option<u16>, String)> {
        let mut result = vec![];
        let (width, _) = dim;
        for (line_number, raw_line) in self.lines.iter().enumerate() {
            let mut line_start = 0;
            let mut line_end = cmp::min(raw_line.len(), width as usize);
            result.push((Some(line_number as u16),
                         String::from(&raw_line[line_start..line_end])));
            while line_end < raw_line.len() {
                line_start = line_end;
                line_end += cmp::min(raw_line.len() - line_end, width as usize);
                result.push((None,
                             String::from(&raw_line[line_start..line_end])));
            }
        }
        result
    }

    fn current_line(&self) -> &String {
        &self.lines[self.file_cursor.y as usize - 1]
    }

    fn recompute_offsets(&mut self, dim: (i32, i32)) {
        if dim != self.last_dim {
            panic!("AAA I CAN'T DO THIS YET");
        }
    }

    pub fn move_cursor_left(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x > 1 {
            self.file_cursor.x -= 1;
        } else if self.file_cursor.y > 1 {
            self.move_cursor_up(dim);
            self.file_cursor.x = self.current_line().len() as i32 + 1;
        }
    }

    pub fn move_cursor_right(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x <= self.current_line().len() as i32 {
            self.file_cursor.x += 1;
        } else if self.file_cursor.y < self.lines.len() as i32 {
            // Depend on truncation here to clamp to lower multiple of screen width
            self.file_cursor.x = ((self.file_cursor.x - 1) / dim.0) * dim.0 + 1;
            self.move_cursor_down(dim);
        }
    }

    pub fn move_cursor_up(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x > dim.0 {
            self.file_cursor.x -= dim.0;
        } else if self.file_cursor.y > 1 {
            self.file_cursor.y -= 1;
            let extra_lines = self.current_line().len() as i32 / dim.0;
            self.y_offset -= self.current_line().len() as i32 / dim.0;
            self.file_cursor.x += extra_lines * dim.0;
            if self.file_cursor.x > self.current_line().len() as i32 + 1 {
                self.file_cursor.x = self.current_line().len() as i32 + 1;
            }
        }
    }

    pub fn move_cursor_down(&mut self, dim: (i32, i32)) {
        if self.file_cursor.x / dim.0 < self.current_line().len() as i32 / dim.0 {
            self.file_cursor.x += dim.0;
        } else if self.file_cursor.y < self.lines.len() as i32 {
            self.file_cursor.y += 1;
            while self.file_cursor.x > dim.0 {
                self.file_cursor.x -= dim.0;
                self.y_offset += 1;
            }
        }
        if self.file_cursor.x > self.current_line().len() as i32 + 1 {
            self.file_cursor.x = self.current_line().len() as i32 + 1;
        }
    }

    pub fn insert(&mut self, dim: (i32, i32), c: char) {
        {
            let x = self.file_cursor.x - 1;
            let line = &mut self.lines[self.file_cursor.y as usize - 1];
            let pos = if x > line.len() as i32 {
                self.file_cursor.x = line.len() as i32 - 1;
                line.len()
            } else {
                x as usize
            };
            line.insert(pos, c);
        }
        self.move_cursor_right(dim);
    }
}
<|endoftext|>


<commit_before>/// Implements a heap data structure (a.k.a. a priority queue).
///
/// In this implementation, heap is "top-heavy" meaning that the root node is
/// the node with the highest value in the heap. The relative priority of
/// two nodes is determined via the `cmp` function defined over type of the
/// heap's elements (i.e. the generic type `T`).
///
/// The allocated memory used to store the elements of the heap grows (and
/// shrinks) as necessary.
///
/// Method naming conventions generally follow those found in `std::vec::Vec`.
///
/// The heap can contain more than one element with the same priority. No
/// guarantees are made about the order in which elements with equivalent
/// priorities are popped from the queue.
///
/// The data sturcture can be output in Graphviz `.dot` format.

// This data structure is implemented as a vector-backed binary heap. (The
// parent-child relationships are therefore not stored via pointers between
// nodes, but using logical connections between NodeIdx values.
//
// See [Wikipedia](http://en.wikipedia.org/wiki/Heap_%28data_structure%29)
// for overview of this implementation strategy.
//
// A simple C implementation of a binary heap is available from
// [here](https://github.com/dale48/levawc). This code is adapted from Chapter
// 10 of O'Reilly book *Mastering Algorithms with C*. This chapter also served
// as a guide while implementing this module.

extern crate graphviz;
extern crate core;

use std;
use std::ptr;
use std::fmt;
use std::fmt::Debug;
use self::core::borrow::IntoCow;

pub type NodeIdx = usize;

pub struct Heap<T: Ord> {
    store: Vec<T>,
}

#[derive(Debug)]
enum ChildType {
    Left,
    Right
}

fn left_child(i: NodeIdx) -> NodeIdx {
    2 * i + 1
}

fn right_child(i: NodeIdx) -> NodeIdx {
    2 * i + 2
}

impl<T: Ord> Heap<T> {
    /// Creates a new empty heap.
    pub fn new() -> Heap<T> {
        Heap { store: Vec::new() }
    }

    /// Creates a new empty heap which has initially allocated enough memory
    /// for the given number of elements.
    pub fn with_capacity(capacity: usize) -> Heap<T> {
        Heap { store: Vec::with_capacity(capacity) }
    }

    /// Adds the given element to the heap.
    pub fn push(&mut self, elem: T) {
        let len = (*self).store.len();
        (*self).store.push(elem);
        let insert_idx: NodeIdx = len as NodeIdx;
        (*self).percolate_up(insert_idx);
    }

    /// Removes from the heap an element with the largest priority of all in
    /// the heap. This element is then returned wrapped returns it wrapped in
    /// an `Option<T>`. If there are no elements in the heap, then `None` is
    /// returned.
    pub fn pop(&mut self) -> Option<T> {
        match (*self).store.len() {
            0 => None,
            1 => (*self).store.pop(),
            _ => {
                let rv = (*self).store.swap_remove(0);
                (*self).percolate_down(0);
                Some(rv)
            }
        }
    }

    /// Returns the number of elements in the heap.
    pub fn len(&self) -> usize {
        (*self).store.len()
    }

    /// Returns `true` iff there are no elements in the heap.
    pub fn empty(&self) -> bool {
        (*self).len() == 0
    }

    /// Takes the index of a node and returns the index of its parent. Returns
    /// `None` if the given node has no such parent (i.e. the given node is
    /// the root.
    ///
    /// The function panics if the given index is not valid.
    fn parent(&self, idx: NodeIdx) -> Option<NodeIdx> {
        if (*self).is_valid(idx) {
            if idx == 0 { None } else { Some((idx - 1) / 2) }
        } else {
            panic!("Heap.parent({}): given `idx` not in the heap.", idx)
        }
    }

    fn left_child(&self, parent: NodeIdx) -> Option<NodeIdx> {
        (*self).child(ChildType::Left, parent)
    }

    fn right_child(&self, parent: NodeIdx) -> Option<NodeIdx> {
        (*self).child(ChildType::Right, parent)
    }

    /// Takes the index of a node and returns the index of indicated child.
    /// Returns `None` if the given node has no such child.
    ///
    /// The function panics if the given index is not valid.
    fn child(&self, ct: ChildType, parent: NodeIdx) -> Option<NodeIdx> {
        if (*self).is_valid(parent) {
            let child: NodeIdx = match ct {
                ChildType::Left  => left_child(parent),
                ChildType::Right => right_child(parent)
            };
            if child < (*self).store.len() {
                Some(child) }
            else {
                None
            }
        } else {
            panic!("Heap.child({:?}, {:?}): the given `idx` is not in `Heap`.",
                   ct, parent)
        }
    }

    /// Starting from the given `NodeIdx`, recursively move an element up the
    /// heap until the heap property has been restored all along this node's
    /// ancestor path.
    fn percolate_up(&mut self, child: NodeIdx) {
        let maybe_parent = (*self).parent(child);
        match maybe_parent {
            None => {
                // Do nothing: The given `child` has no parent because it is
                // the root node.
                return
            },
            Some(parent) => {
                if (*self).is_violating(parent, child) {
                    (*self).swap(parent, child);
                    (*self).percolate_up(parent)
                } else {
                    // Do nothing: the two nodes are already ordered correctly.
                    return
                }
            }
        }
    }

    /// Starting from the given `NodeIdx`, recursively move an element down
    /// the heap until the heap property has been restored in the entire
    /// sub-heap.
    ///
    /// (For the heap property to be restored to the entire sub-heap being
    /// re-heapified, the only element which may be violating the heap-property
    /// is the node indicated by the given `NodeIdx`.)
    fn percolate_down(&mut self, parent: NodeIdx) {
        match ((*self).left_child(parent), (*self).right_child(parent)) {
            (None, None)        => return,
            (None, Some(right)) => panic!("Heap can't only have right child."),
            (Some(left), None)  => {
                if (*self).is_violating(parent, left) {
                    (*self).swap_down(parent, left)
                }
            },
            (Some(left), Some(right)) => {
                match ((*self).is_violating(parent, left),
                       (*self).is_violating(parent, right)) {
                    (false, false) => return,
                    (false, true)  => (*self).swap_down(parent, right),
                    (true,  false) => (*self).swap_down(parent, left),
                    (true,  true)  => {
                        // Since both of the parent's children are violating
                        // the heap property, choose which child should be
                        // swapped with the parent, such that the heap property
                        // will not be violated after the swap. (That is, the
                        // greater of the two will need to become the parent of
                        // the other.)
                        if (*self).store[left] >= (*self).store[right] {
                            (*self).swap_down(parent, left)
                        } else {
                            (*self).swap_down(parent, right)
                        }
                    }
                }
            }
        }
    }

    /// Helper function for `percolate_down()`.
    fn swap_down(&mut self, parent: NodeIdx, child: NodeIdx) {
        (*self).swap(parent, child);
        (*self).percolate_down(child);
    }

    /// Checks to see whether the given parent-child nodes are violating the
    /// heap property.
    ///
    /// Panics if either index is out of bounds. Panics if the given parent is
    /// not actually the parent of the given child.
    fn is_violating(&self, parent: NodeIdx, child: NodeIdx) -> bool{
        if parent == (*self).parent(child).unwrap() {
            (*self).store[parent] < (*self).store[child]
        } else {
            panic!("Given parent is not actually the parent of this child.")
        }
    }

    fn is_valid(&self, idx: NodeIdx) -> bool {
        idx < (*self).store.len()
    }

    /// Swaps the data stored at the two inciated heap nodes.
    ///
    /// Does nothing if the two indices are the same. Panics if either index is
    /// invalid.
    fn swap(&mut self, a: NodeIdx, b: NodeIdx) {
        if a != b {
            unsafe {
                let pa: *mut T = &mut self.store[a];
                let pb: *mut T = &mut self.store[b];
                ptr::swap(pa, pb);
            }
        }
    }
}

pub type Edge = (NodeIdx, NodeIdx);

impl<'a, T: Ord + 'a> graphviz::GraphWalk<'a, NodeIdx, Edge> for Heap<T> {
    fn nodes(&self) -> graphviz::Nodes<'a, NodeIdx> {
        let mut v: Vec<NodeIdx> = Vec::new();
        for node_idx in (0..self.len()) {
            v.push(node_idx);
        }
        v.into_cow()
    }

    fn edges(&'a self) -> graphviz::Edges<'a, Edge> {
        let mut v: Vec<Edge> = Vec::with_capacity(2 * self.len());
        // Add an edge for each parent-child relationship in the heap.
        for idx in (0..self.len()) {
            match self.left_child(idx) {
                Some(l) => v.push((idx, l)),
                None    => ()
            };
            match self.right_child(idx) {
                Some(r) => v.push((idx, r)),
                None    => ()
            };
        }
        v.into_cow()
    }

    fn source(&self, edge: &Edge) -> NodeIdx {
        let &(s, _) = edge;
        s
    }

    fn target(&self, edge: &Edge) -> NodeIdx {
        let &(_, t) = edge;
        t
    }
}

impl<'a, T: 'a + Ord + fmt::Debug> graphviz::Labeller<'a, NodeIdx, Edge> for Heap<T> {
    fn graph_id(&'a self) -> graphviz::Id<'a> {
        graphviz::Id::new("Heap").unwrap()
    }

    fn node_id(&'a self, n: &NodeIdx) -> graphviz::Id<'a> {
        graphviz::Id::new(format!("n{}", n)).unwrap()
    }

    fn node_label(&'a self, n: &NodeIdx) -> graphviz::LabelText<'a> {
        let label = format!("{:?}", self.store[*n]);
        graphviz::LabelText::LabelStr(label.into_cow())
    }
}
<commit_msg>Converted `(*self).member` syntax to `self.member`.<commit_after>/// Implements a heap data structure (a.k.a. a priority queue).
///
/// In this implementation, heap is "top-heavy" meaning that the root node is
/// the node with the highest value in the heap. The relative priority of
/// two nodes is determined via the `cmp` function defined over type of the
/// heap's elements (i.e. the generic type `T`).
///
/// The allocated memory used to store the elements of the heap grows (and
/// shrinks) as necessary.
///
/// Method naming conventions generally follow those found in `std::vec::Vec`.
///
/// The heap can contain more than one element with the same priority. No
/// guarantees are made about the order in which elements with equivalent
/// priorities are popped from the queue.
///
/// The data sturcture can be output in Graphviz `.dot` format.

// This data structure is implemented as a vector-backed binary heap. (The
// parent-child relationships are therefore not stored via pointers between
// nodes, but using logical connections between NodeIdx values.
//
// See [Wikipedia](http://en.wikipedia.org/wiki/Heap_%28data_structure%29)
// for overview of this implementation strategy.
//
// A simple C implementation of a binary heap is available from
// [here](https://github.com/dale48/levawc). This code is adapted from Chapter
// 10 of O'Reilly book *Mastering Algorithms with C*. This chapter also served
// as a guide while implementing this module.

extern crate graphviz;
extern crate core;

use std;
use std::ptr;
use std::fmt;
use std::fmt::Debug;
use self::core::borrow::IntoCow;

pub type NodeIdx = usize;

pub struct Heap<T: Ord> {
    store: Vec<T>,
}

#[derive(Debug)]
enum ChildType {
    Left,
    Right
}

fn left_child(i: NodeIdx) -> NodeIdx {
    2 * i + 1
}

fn right_child(i: NodeIdx) -> NodeIdx {
    2 * i + 2
}

impl<T: Ord> Heap<T> {
    /// Creates a new empty heap.
    pub fn new() -> Heap<T> {
        Heap { store: Vec::new() }
    }

    /// Creates a new empty heap which has initially allocated enough memory
    /// for the given number of elements.
    pub fn with_capacity(capacity: usize) -> Heap<T> {
        Heap { store: Vec::with_capacity(capacity) }
    }

    /// Adds the given element to the heap.
    pub fn push(&mut self, elem: T) {
        let len = self.store.len();
        self.store.push(elem);
        let insert_idx: NodeIdx = len as NodeIdx;
        self.percolate_up(insert_idx);
    }

    /// Removes from the heap an element with the largest priority of all in
    /// the heap. This element is then returned wrapped returns it wrapped in
    /// an `Option<T>`. If there are no elements in the heap, then `None` is
    /// returned.
    pub fn pop(&mut self) -> Option<T> {
        match self.store.len() {
            0 => None,
            1 => self.store.pop(),
            _ => {
                let rv = self.store.swap_remove(0);
                self.percolate_down(0);
                Some(rv)
            }
        }
    }

    /// Returns the number of elements in the heap.
    pub fn len(&self) -> usize {
        self.store.len()
    }

    /// Returns `true` iff there are no elements in the heap.
    pub fn empty(&self) -> bool {
        self.len() == 0
    }

    /// Takes the index of a node and returns the index of its parent. Returns
    /// `None` if the given node has no such parent (i.e. the given node is
    /// the root.
    ///
    /// The function panics if the given index is not valid.
    fn parent(&self, idx: NodeIdx) -> Option<NodeIdx> {
        if self.is_valid(idx) {
            if idx == 0 { None } else { Some((idx - 1) / 2) }
        } else {
            panic!("Heap.parent({}): given `idx` not in the heap.", idx)
        }
    }

    fn left_child(&self, parent: NodeIdx) -> Option<NodeIdx> {
        self.child(ChildType::Left, parent)
    }

    fn right_child(&self, parent: NodeIdx) -> Option<NodeIdx> {
        self.child(ChildType::Right, parent)
    }

    /// Takes the index of a node and returns the index of indicated child.
    /// Returns `None` if the given node has no such child.
    ///
    /// The function panics if the given index is not valid.
    fn child(&self, ct: ChildType, parent: NodeIdx) -> Option<NodeIdx> {
        if self.is_valid(parent) {
            let child: NodeIdx = match ct {
                ChildType::Left  => left_child(parent),
                ChildType::Right => right_child(parent)
            };
            if child < self.store.len() {
                Some(child) }
            else {
                None
            }
        } else {
            panic!("Heap.child({:?}, {:?}): the given `idx` is not in `Heap`.",
                   ct, parent)
        }
    }

    /// Starting from the given `NodeIdx`, recursively move an element up the
    /// heap until the heap property has been restored all along this node's
    /// ancestor path.
    fn percolate_up(&mut self, child: NodeIdx) {
        let maybe_parent = self.parent(child);
        match maybe_parent {
            None => {
                // Do nothing: The given `child` has no parent because it is
                // the root node.
                return
            },
            Some(parent) => {
                if self.is_violating(parent, child) {
                    self.swap(parent, child);
                    self.percolate_up(parent)
                } else {
                    // Do nothing: the two nodes are already ordered correctly.
                    return
                }
            }
        }
    }

    /// Starting from the given `NodeIdx`, recursively move an element down
    /// the heap until the heap property has been restored in the entire
    /// sub-heap.
    ///
    /// (For the heap property to be restored to the entire sub-heap being
    /// re-heapified, the only element which may be violating the heap-property
    /// is the node indicated by the given `NodeIdx`.)
    fn percolate_down(&mut self, parent: NodeIdx) {
        match (self.left_child(parent), self.right_child(parent)) {
            (None, None)        => return,
            (None, Some(right)) => panic!("Heap can't only have right child."),
            (Some(left), None)  => {
                if self.is_violating(parent, left) {
                    self.swap_down(parent, left)
                }
            },
            (Some(left), Some(right)) => {
                match (self.is_violating(parent, left),
                       self.is_violating(parent, right)) {
                    (false, false) => return,
                    (false, true)  => self.swap_down(parent, right),
                    (true,  false) => self.swap_down(parent, left),
                    (true,  true)  => {
                        // Since both of the parent's children are violating
                        // the heap property, choose which child should be
                        // swapped with the parent, such that the heap property
                        // will not be violated after the swap. (That is, the
                        // greater of the two will need to become the parent of
                        // the other.)
                        if self.store[left] >= self.store[right] {
                            self.swap_down(parent, left)
                        } else {
                            self.swap_down(parent, right)
                        }
                    }
                }
            }
        }
    }

    /// Helper function for `percolate_down()`.
    fn swap_down(&mut self, parent: NodeIdx, child: NodeIdx) {
        self.swap(parent, child);
        self.percolate_down(child);
    }

    /// Checks to see whether the given parent-child nodes are violating the
    /// heap property.
    ///
    /// Panics if either index is out of bounds. Panics if the given parent is
    /// not actually the parent of the given child.
    fn is_violating(&self, parent: NodeIdx, child: NodeIdx) -> bool{
        if parent == self.parent(child).unwrap() {
            self.store[parent] < self.store[child]
        } else {
            panic!("Given parent is not actually the parent of this child.")
        }
    }

    fn is_valid(&self, idx: NodeIdx) -> bool {
        idx < self.store.len()
    }

    /// Swaps the data stored at the two inciated heap nodes.
    ///
    /// Does nothing if the two indices are the same. Panics if either index is
    /// invalid.
    fn swap(&mut self, a: NodeIdx, b: NodeIdx) {
        if a != b {
            unsafe {
                let pa: *mut T = &mut self.store[a];
                let pb: *mut T = &mut self.store[b];
                ptr::swap(pa, pb);
            }
        }
    }
}

pub type Edge = (NodeIdx, NodeIdx);

impl<'a, T: Ord + 'a> graphviz::GraphWalk<'a, NodeIdx, Edge> for Heap<T> {
    fn nodes(&self) -> graphviz::Nodes<'a, NodeIdx> {
        let mut v: Vec<NodeIdx> = Vec::new();
        for node_idx in (0..self.len()) {
            v.push(node_idx);
        }
        v.into_cow()
    }

    fn edges(&'a self) -> graphviz::Edges<'a, Edge> {
        let mut v: Vec<Edge> = Vec::with_capacity(2 * self.len());
        // Add an edge for each parent-child relationship in the heap.
        for idx in (0..self.len()) {
            match self.left_child(idx) {
                Some(l) => v.push((idx, l)),
                None    => ()
            };
            match self.right_child(idx) {
                Some(r) => v.push((idx, r)),
                None    => ()
            };
        }
        v.into_cow()
    }

    fn source(&self, edge: &Edge) -> NodeIdx {
        let &(s, _) = edge;
        s
    }

    fn target(&self, edge: &Edge) -> NodeIdx {
        let &(_, t) = edge;
        t
    }
}

impl<'a, T: 'a + Ord + fmt::Debug> graphviz::Labeller<'a, NodeIdx, Edge> for Heap<T> {
    fn graph_id(&'a self) -> graphviz::Id<'a> {
        graphviz::Id::new("Heap").unwrap()
    }

    fn node_id(&'a self, n: &NodeIdx) -> graphviz::Id<'a> {
        graphviz::Id::new(format!("n{}", n)).unwrap()
    }

    fn node_label(&'a self, n: &NodeIdx) -> graphviz::LabelText<'a> {
        let label = format!("{:?}", self.store[*n]);
        graphviz::LabelText::LabelStr(label.into_cow())
    }
}
<|endoftext|>


<commit_before>extern crate git2;
extern crate clap;
use git2::*;
use std::process::Command;
use std::process::exit;
use std::env;
use std::path::Path;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload() { }

fn central_review_upload() { }
fn central_submit() { }
fn central_direct_push() { }

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stdout));
}

fn setup_tmp_repo(name: &str, modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(name).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stdout));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(module: Tree, master: Tree) {
  let modules_oid = master.get_path(&Path::new("modules")).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  // let mbuilder = TreeBuilder::new(modules_tree);
  // let mbuilder = tmp_repo.treebuilder(modules_tree);
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn main() {

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    if !is_review && !is_automation {
      println!("only push to refs/for/master");
      exit(1);
    }

    if is_module && is_update && is_review { module_review_upload(); }
    if !is_module && is_update && is_review { central_review_upload(); }
    if !is_module && is_update && !is_review { central_direct_push(); }
    if !is_module && is_submit { central_submit(); }
  }

  let repo = match Repository::open(".") {
      Ok(repo) => repo,
      Err(e) => panic!("failed to init: {}", e),
  };
  println!("Hello, world!");
  exit(0);
}
<commit_msg>splitting works<commit_after>extern crate git2;
extern crate clap;
use git2::*;
use std::process::Command;
use std::process::exit;
use std::env;
use std::path::Path;
use std::path::PathBuf;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload() { }

fn central_review_upload() { }
fn central_submit() { }

fn central_direct_push(newrev: &str) {
  println!("central_direct_push");
  let mpath = Path::new("modules");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let modules = tmp_repo.find_tree(central_tree.get_path(mpath).unwrap().id());
  tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
      );
      println!("{}", x);
    //         print("{0}: pushed commit {1}".format(module_name, module_commit))
    }
    else{

    //         print("{0}: no changes".format(module_name))
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
  .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
  .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
  .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stdout));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stdout));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0040000); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0040000); // GIT_FILEMODE_TREE
  return builder.write().unwrap();
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  tmp_repo.set_head_detached(commit.id());
  return in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
      AUTOMATION_USER,
      repo,
      to
    )
  )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
  ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // if !is_review && !is_automation {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_module && is_update && is_review { module_review_upload(); }
    if !is_module && is_update && is_review { central_review_upload(); }
    if !is_module && is_update && !is_review { central_direct_push(newrev); }
    if !is_module && is_submit { central_submit(); }
  }

  let repo = match Repository::open(".") {
      Ok(repo) => repo,
      Err(e) => panic!("failed to init: {}", e),
  };
  println!("Hello, world!");
  return 0;
}

<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::fs::File;
use std::collections::HashMap;
use std::os::unix::fs::MetadataExt;
use std::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
use std::io::Read;

#[derive(Debug)]
struct FileEntry {
    inode: u64,
    size: u64,
    dev:  u64,
    path : String,
}

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<FileEntry>>) {
    if let Ok(entries) = fs::read_dir(dir) {
        for entry in entries {
            if let Ok(entry) = entry {
                let path = entry.path();
                if let Some(path_str) = path.to_str() {
                    if let Ok(metadata) = path.symlink_metadata() {
                        let ft = metadata.file_type();
                        if !ft.is_symlink() {
                            if ft.is_file() {
                                let file_size = metadata.len();
                                if !h.contains_key(&file_size) {
                                    h.insert(file_size, Vec::new());
                                }
                                if let Some(vec) = h.get_mut(&file_size) {
                                    vec.push(
                                         FileEntry{ inode : metadata.ino(),
                                             size  : metadata.len(),
                                             dev   : metadata.dev(),
                                             path  : String::from(path_str),
                                         });
                                }
                            } else if ft.is_dir() {
                                collect_files(&(String::from(path_str)), h);
                            }
                        }
                    }
                }
            }
        }
    }
    else {
        println!("{}: invalid directory", dir);
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<FileEntry>>,
                   vec: &Vec<FileEntry> ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(&file_entry.path) {
            let mut buf: [u8; 1024] = [0; 1024];
            if let Ok(nbytes) = file.read(&mut buf) {
                let mut hasher = DefaultHasher::new();
                hasher.write(&buf[..nbytes as usize]);
                let k = hasher.finish();
                if !duplicates.contains_key(&k) {
                    duplicates.insert(k, Vec::new());
                }
                if let Some(vec) = duplicates.get_mut(&k) {
                    vec.push(
                        FileEntry{ inode : file_entry.inode,
                            size : file_entry.size,
                            dev  : file_entry.dev,
                            path : file_entry.path.to_string(),
                        });
                }
            }
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Print out usage if no directories are given.
    if args.len() == 1 {
        println!("Usage: {} <dir1> [dir2 [dir3 ...]]", args[0]);
        ::std::process::exit(0);
    }

    let mut hmap: HashMap<u64, Vec<FileEntry> > = HashMap::new();

    for dir in &args[1..] {
        collect_files(dir, &mut hmap);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap.iter() {
        if *key == 0 {
            continue;
        }
        let mut duplicates: HashMap<u64, Vec<FileEntry> > = HashMap::new();
        find_duplicates(&mut duplicates, &val);
        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                println!("{} files in cluster {} (size: {}, digest: {})",
                         vec.len(), cluster, key, hash);
                for file_entry in vec {
                    println!("{}", file_entry.path);
                }
                cluster += 1;
            }
        }
    }
}
<commit_msg>Filter out zero sized files in the collect phase<commit_after>use std::fs;
use std::env;
use std::fs::File;
use std::collections::HashMap;
use std::os::unix::fs::MetadataExt;
use std::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
use std::io::Read;

#[derive(Debug)]
struct FileEntry {
    inode: u64,
    size: u64,
    dev:  u64,
    path : String,
}

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<FileEntry>>) {
    if let Ok(entries) = fs::read_dir(dir) {
        for entry in entries {
            if let Ok(entry) = entry {
                let path = entry.path();
                if let Some(path_str) = path.to_str() {
                    if let Ok(metadata) = path.symlink_metadata() {
                        let ft = metadata.file_type();
                        if !ft.is_symlink() {
                            if ft.is_file() {
                                let file_size = metadata.len();
                                if file_size == 0 {
                                    continue;
                                }
                                if !h.contains_key(&file_size) {
                                    h.insert(file_size, Vec::new());
                                }
                                if let Some(vec) = h.get_mut(&file_size) {
                                    vec.push(
                                         FileEntry{ inode : metadata.ino(),
                                             size  : metadata.len(),
                                             dev   : metadata.dev(),
                                             path  : String::from(path_str),
                                         });
                                }
                            } else if ft.is_dir() {
                                collect_files(&(String::from(path_str)), h);
                            }
                        }
                    }
                }
            }
        }
    }
    else {
        println!("{}: invalid directory", dir);
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<FileEntry>>,
                   vec: &Vec<FileEntry> ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(&file_entry.path) {
            let mut buf: [u8; 1024] = [0; 1024];
            if let Ok(nbytes) = file.read(&mut buf) {
                let mut hasher = DefaultHasher::new();
                hasher.write(&buf[..nbytes as usize]);
                let k = hasher.finish();
                if !duplicates.contains_key(&k) {
                    duplicates.insert(k, Vec::new());
                }
                if let Some(vec) = duplicates.get_mut(&k) {
                    vec.push(
                        FileEntry{ inode : file_entry.inode,
                            size : file_entry.size,
                            dev  : file_entry.dev,
                            path : file_entry.path.to_string(),
                        });
                }
            }
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Print out usage if no directories are given.
    if args.len() == 1 {
        println!("Usage: {} <dir1> [dir2 [dir3 ...]]", args[0]);
        ::std::process::exit(0);
    }

    let mut hmap: HashMap<u64, Vec<FileEntry> > = HashMap::new();

    for dir in &args[1..] {
        collect_files(dir, &mut hmap);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap.iter() {
        let mut duplicates: HashMap<u64, Vec<FileEntry> > = HashMap::new();
        find_duplicates(&mut duplicates, &val);
        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                println!("{} files in cluster {} (size: {}, digest: {})",
                         vec.len(), cluster, key, hash);
                for file_entry in vec {
                    println!("{}", file_entry.path);
                }
                cluster += 1;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate case;
extern crate docopt;
extern crate env_logger;
extern crate git2;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate xml;
extern crate toml;

use env::Env;
use library::Library;

//generated by build.rs
mod gir_version;

mod analysis;
mod codegen;
mod config;
mod env;
mod file_saver;
mod git;
mod gobjects;
mod library;
mod nameutil;
mod parser;
mod traits;
mod version;

#[cfg_attr(test, allow(dead_code))]
fn main() {
    env_logger::init().unwrap_or_else(|e| panic!("{}", e));

    let cfg = match config::Config::new() {
        Ok(c) => c,
        Err(config::Error::CommandLine(e)) => e.exit(),
        Err(e) => {
            println!("{}", e);
            ::std::process::exit(1);
        }
    };

    let mut library = Library::new(&cfg.library_name);
    library.read_file(&cfg.girs_dir, &cfg.library_full_name());
    library.fill_in();

    let env = Env{ library: library, config: cfg };
    codegen::generate(&env);
}
<commit_msg>Unify error handling in main<commit_after>extern crate case;
extern crate docopt;
extern crate env_logger;
extern crate git2;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate xml;
extern crate toml;

use std::error::Error;
use std::process;

use env::Env;
use library::Library;

//generated by build.rs
mod gir_version;

mod analysis;
mod codegen;
mod config;
mod env;
mod file_saver;
mod git;
mod gobjects;
mod library;
mod nameutil;
mod parser;
mod traits;
mod version;

#[cfg_attr(test, allow(dead_code))]
fn main() {
    if let Err(err) = do_main() {
        println!("{}", err);
        process::exit(1);
    }
}

fn do_main() -> Result<(), Box<Error>> {
    try!(env_logger::init());

    let cfg = match config::Config::new() {
        Ok(cfg) => cfg,
        Err(config::Error::CommandLine(ref err)) if !err.fatal() => return Ok(()),
        Err(err) => return Err(Box::new(err)),
    };

    let mut library = Library::new(&cfg.library_name);
    library.read_file(&cfg.girs_dir, &cfg.library_full_name());
    library.fill_in();

    let env = Env{ library: library, config: cfg };
    codegen::generate(&env);

    Ok(())
}
<|endoftext|>


<commit_before>use std::env;
use std::path::*;

enum Bitness {
    B32,
    B64
}

struct Vm {
    name: String,
    bitness: Bitness,
}

fn get_yogurt_workspace_path() ->  Option<PathBuf> {
    match env::home_dir() {
        Some(mut home_dir) => {
            home_dir.push(".yogurt");
            Some(home_dir)
        },
        None => None
    }
}

fn get_available_vms() -> Vec<Vm> {
    let mut vms = Vec::new();
    vms.push(Vm{ name: String::from("20170708"), bitness: Bitness::B64 });

    return vms
}

fn main() {
    match get_yogurt_workspace_path() {
        Some(path) => println!("Yogurt storage: {:?}", path),
        None => panic!("Could not find Yogurt working directory. Abort    ")
    }

    println!("Available VMs:");
    for vm in get_available_vms() {
        println!("- {}", vm.name);
    }
}
<commit_msg>Create workspace directories<commit_after>use std::env;
use std::fs;
use std::path::*;

enum Bitness {
    B32,
    B64
}

struct Vm {
    name: String,
    bitness: Bitness,
}

fn yogurt_workspace_path() ->  Option<PathBuf> {
    match env::home_dir() {
        Some(mut home_dir) => {
            home_dir.push(".yogurt");
            Some(home_dir)
        },
        None => None
    }
}

fn vms_dir() -> PathBuf {
    yogurt_workspace_path()
        .expect("Could not find workspace directory")
        .join("vms")
}

fn images_dir() -> PathBuf {
    yogurt_workspace_path()
        .expect("Could not find workspace directory")
        .join("images")
}

fn sources_dir() -> PathBuf {
    yogurt_workspace_path()
        .expect("Could not find workspace directory")
        .join("sources")
}

fn create_dirs_if_necessary() {
    fs::create_dir_all(vms_dir()).expect("Could not create VMs directory. Abort");
    fs::create_dir_all(images_dir()).expect("Could not create images directory. Abort");
    fs::create_dir_all(sources_dir()).expect("Could not create sources directory. Abort");
}

fn available_vms() -> Vec<Vm> {
    let mut vms = Vec::new();
    let dirs_to_scan = fs::read_dir(vms_dir()).unwrap();

    for entry in dirs_to_scan {
        let path = entry.unwrap().path();
        let dir_name = path.file_name().unwrap().to_str().unwrap();

        if path.is_dir() {
            vms.push(Vm{ name: String::from(dir_name), bitness: Bitness::B64});
        }
    }

    return vms
}

fn print_available_vms() {
    println!("Available VMs:");
    for vm in available_vms() {
        println!("- {}", vm.name);
    }
}

fn main() {
    create_dirs_if_necessary();
    print_available_vms();
}
<|endoftext|>