<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("07.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

type Graph<T> = HashMap<T, HashSet<T>>;
type Timetable<T> = HashMap<usize, HashMap<usize, T>>;

fn parse_input(input: &str) -> Graph<char> {
    fn first_char(x: &str) -> Option<char> {
        x.chars().next()
    }

    input.lines()
    .map(|line| line.split(' '))
    .filter_map(|mut tokens| {
        tokens.nth(1).and_then(first_char)
        .and_then(|x| tokens.nth(5).and_then(first_char).map(|y| (x, y)))
    })
    .fold(Graph::new(), |mut graph, (x, y)| {
        if !graph.contains_key(&x) {
            graph.insert(x, HashSet::new());
        }

        let mut children = graph.remove(&x).unwrap();
        children.insert(y);

        for &child in children.iter() {
            if !graph.contains_key(&child) {
                graph.insert(child, HashSet::new());
            }
        }

        graph.insert(x, children);
        graph
    })
}

fn get_root_nodes<T: Eq + Hash + Copy>(graph: &Graph<T>) -> Vec<T> {
    graph.keys()
    .cloned()
    .filter(|&k| {
        graph.values()
        .flat_map(|children| children.iter())
        .all(|&child| child != k)
    })
    .collect()
}

fn get_order<T: Eq + Hash + Copy + Ord>(mut graph: Graph<T>) -> Vec<T> {
    let mut result = vec![];
    let mut root_nodes;

    loop {
        root_nodes = get_root_nodes(&graph);
        root_nodes.sort();

        if root_nodes.len() == 0 {
            break;
        }

        let node = root_nodes.remove(0);

        graph.remove(&node);
        result.push(node);
    }

    result
}

fn allocate<T>(timetable: &mut Timetable<T>, node: T, worker: usize, time: usize, duration: usize)
where T: Copy {
    for t in time..time + duration {
        if !timetable.contains_key(&t) {
            timetable.insert(t, HashMap::new());
        }

        let workers = timetable.get_mut(&t).unwrap();
        workers.insert(worker, node);
    }
}

fn has_time<T>(timetable: &mut Timetable<T>, worker: usize, time: usize) -> bool {
    timetable.get(&time)
    .and_then(|workers| workers.get(&worker))
    .is_some()
}

fn create_timetable<T>(graph: Graph<T>, times: &HashMap<T, usize>, workers: usize) -> Timetable<T>
where T: Eq + Hash + Copy + Ord {
    let mut result = Timetable::new();
    let mut root_nodes;

    loop {
        root_nodes = get_root_nodes(&graph);
        root_nodes.sort();
    }

    result
}

fn main() {
    let input = get_input().unwrap();
    let graph = parse_input(&input);
    let order = get_order(graph);

    println!("Part 1: {}", order.into_iter().fold(String::new(), |mut acc, x| {
        acc.push(x);
        acc
    }));
}
<commit_msg>Add 2018 day 7 part 2 solution<commit_after>use std::cmp::min;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("07.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

type Graph<T> = HashMap<T, HashSet<T>>;

struct Timetable<'a, T: 'a>
where T: Hash + Eq {
    durations: &'a HashMap<T, usize>,
    by_time: HashMap<usize, HashMap<usize, T>>,
    stop_times: HashMap<T, usize>
}

impl<'a, T: 'a> Timetable<'a, T>
where T: Hash + Eq + Copy {
    fn new(durations: &HashMap<T, usize>) -> Timetable<T> {
        Timetable {
            durations,
            by_time: HashMap::new(),
            stop_times: HashMap::new()
        }
    }

    fn allocate(&mut self, job: T, worker: usize, time: usize) {
        let duration = self.durations.get(&job).cloned().unwrap_or(1);

        if self.stop_times.contains_key(&job) {
            return;
        }

        for t in time..time + duration {
            if !self.by_time.contains_key(&t) {
                self.by_time.insert(t, HashMap::new());
            }

            let worker_data = self.by_time.get_mut(&t).unwrap();
            worker_data.insert(worker, job);
        }

        self.stop_times.insert(job, time + duration);
    }
}

impl<'a> fmt::Debug for Timetable<'a, char> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Timetable {{")?;

        for t in 0..self.by_time.keys().cloned().max().unwrap_or(0) + 1 {
            write!(f, "  ({:4}) ", t)?;

            if let Some(worker_data) = self.by_time.get(&t) {
                for id in 0..worker_data.keys().cloned().max().unwrap_or(0) + 1 {
                    write!(f, "{:?} ", worker_data.get(&id).cloned().unwrap_or(' '))?;
                }
            }

            writeln!(f, "")?;
        }

        write!(f, "}}")?;
        Ok(())
    }
}

fn parse_input(input: &str) -> Graph<char> {
    fn first_char(x: &str) -> Option<char> {
        x.chars().next()
    }

    input.lines()
    .map(|line| line.split(' '))
    .filter_map(|mut tokens| {
        tokens.nth(1).and_then(first_char)
        .and_then(|x| tokens.nth(5).and_then(first_char).map(|y| (x, y)))
    })
    .fold(Graph::new(), |mut graph, (x, y)| {
        if !graph.contains_key(&x) {
            graph.insert(x, HashSet::new());
        }

        let mut children = graph.remove(&x).unwrap();
        children.insert(y);

        for &child in children.iter() {
            if !graph.contains_key(&child) {
                graph.insert(child, HashSet::new());
            }
        }

        graph.insert(x, children);
        graph
    })
}

fn get_root_nodes<T>(graph: &Graph<T>) -> Vec<T>
where T: Eq + Hash + Copy {
    graph.keys()
    .cloned()
    .filter(|&k| {
        graph.values()
        .flat_map(|children| children.iter())
        .all(|&child| child != k)
    })
    .collect()
}

fn get_order<T>(mut graph: Graph<T>) -> Vec<T>
where T: Eq + Hash + Copy + Ord {
    let mut result = vec![];
    let mut root_nodes;

    loop {
        root_nodes = get_root_nodes(&graph);
        root_nodes.sort();

        if root_nodes.len() == 0 {
            break;
        }

        let node = root_nodes.remove(0);

        graph.remove(&node);
        result.push(node);
    }

    result
}

fn create_timetable<T>(mut graph: Graph<T>, durations: &HashMap<T, usize>, workers: usize) -> Timetable<T>
where T: Eq + Hash + Copy + Ord {
    let mut result = Timetable::new(durations);
    let mut root_nodes;
    let mut busy = vec![];
    let mut t = 0;

    loop {
        // Wait for free workers

        loop {
            if !result.by_time.contains_key(&t) {
                result.by_time.insert(t, HashMap::new());
            }

            let busy_workers_count = result.by_time.get(&t).unwrap().keys().count();

            if busy_workers_count >= workers {
                t += 1;
            } else {
                break;
            }
        }

        // Remove jobs that have stopped

        let stopped: Vec<T> = busy.iter().cloned()
            .filter(|&job| result.stop_times.get(&job).cloned().unwrap() <= t)
            .collect();

        for node in stopped.iter() {
            graph.remove(node);
        }

        busy.retain(|job| !stopped.contains(job));

        // Get available jobs

        root_nodes = get_root_nodes(&graph);
        root_nodes.retain(|job| !busy.contains(job));
        root_nodes.sort();

        if root_nodes.len() == 0 {
            if busy.len() == 0 {
                break;
            } else {
                t += 1;
                continue;
            }
        }

        // Allocate jobs

        let free_workers: Vec<usize> = {
            let worker_data = result.by_time.get(&t).unwrap();

            (0..workers)
            .filter(|id| !worker_data.contains_key(id))
            .collect()
        };

        let new_job_count = min(free_workers.len(), root_nodes.len());
        let new_jobs: Vec<T> = root_nodes.iter().cloned().take(new_job_count).collect();

        for (job, worker) in new_jobs.iter().cloned().zip(free_workers.iter().cloned()) {
            result.allocate(job, worker, t);
            busy.push(job);
        }
    }

    result
}

fn main() {
    let input = get_input().unwrap();
    let graph = parse_input(&input);
    let order = get_order(graph.clone());

    println!("Part 1: {}", order.into_iter().fold(String::new(), |mut acc, x| {
        acc.push(x);
        acc
    }));

    let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    let durations = alphabet.chars()
        .enumerate()
        .fold(HashMap::new(), |mut acc, (i, c)| {
            acc.insert(c, i + 61);
            acc
        });
    let timetable = create_timetable(graph, &durations, 5);

    println!("Part 2: {}", timetable.by_time.keys().cloned().max().unwrap_or(0));
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashMap, VecDeque};
use std::hash::Hash;

#[derive(Debug, Copy, Clone)]
enum Tile<T> {
  Wall,
  Passage,
  Door(T),
  Key(T)
}

type Position = (usize, usize);
type Labyrinth<T> = HashMap<Position, Tile<T>>;

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("18.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_labyrinth(input: &str) -> (Labyrinth<char>, Option<Position>) {
  let labyrinth = input.lines()
    .enumerate()
    .flat_map(|(y, line)| {
      line.chars()
      .enumerate()
      .map(move |(x, c)| (
        (x, y),
        match c {
          '#' => Tile::Wall,
          '@' => Tile::Passage,
          c if c.is_ascii_uppercase() => Tile::Door(c.to_ascii_lowercase()),
          c if c.is_ascii_lowercase() => Tile::Key(c),
          _ => Tile::Passage
        }
      ))
    })
    .collect::<Labyrinth<_>>();

  let entrance = input.lines()
    .enumerate()
    .find_map(|(y, line)| {
      line.chars()
      .position(|c| c == '@')
      .map(|x| (x, y))
    });

  (labyrinth, entrance)
}

fn get_neighbors<T: Hash + Eq>(labyrinth: &Labyrinth<T>, (x, y): Position, keys: &Vec<T>) -> Vec<Position> {
  [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
  .cloned()
  .filter(|&(x, y)| x > 0 && y > 0)
  .filter(|pos| match labyrinth.get(pos) {
    Some(Tile::Passage) | Some(Tile::Key(_)) => true,
    Some(Tile::Door(x)) => keys.contains(x),
    _ => false
  })
  .collect()
}

fn get_all_keys(labyrinth: &Labyrinth<char>, position: Position, key_count: usize) -> Vec<Position> {
  let mut queue = VecDeque::new();
  let mut previous_map = HashMap::new();
  let mut target = None;

  queue.push_back((position, vec![]));
  previous_map.insert((position, vec![]), None);

  while let Some((position, keys)) = queue.pop_front() {
    if keys.len() >= key_count {
      target = Some((position, keys));
      break;
    }

    for neighbor in get_neighbors(labyrinth, position, &keys) {
      let mut new_keys = keys.clone();

      if let Some(&Tile::Key(c)) = labyrinth.get(&neighbor) {
        new_keys.push(c);
        new_keys.sort();
        new_keys.dedup();
      }

      let map_key = (neighbor, new_keys);

      if !previous_map.contains_key(&map_key) {
        queue.push_back((neighbor, map_key.1.clone()));
        previous_map.insert(map_key, Some((position, keys.clone())));
      }
    }
  }

  let mut result = vec![target.unwrap()];

  while let Some(Some(prev)) = previous_map.get(&result.last().unwrap()) {
    result.push(prev.clone());
  }

  result.into_iter().rev().map(|(position, _)| position).collect()
}

fn main() {
  let input = get_input().unwrap();
  let (labyrinth, position) = parse_labyrinth(&input);

  let path = get_all_keys(&labyrinth, position.unwrap(), 26);

  println!("Part 1: {}", path.len() - 1);
}
<commit_msg>Add HasNeighbors trait and impls<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::{HashMap, VecDeque};
use std::hash::Hash;

#[derive(Debug, Copy, Clone)]
enum Tile<K> {
  Wall,
  Passage,
  Door(K),
  Key(K)
}

type Position = (usize, usize);
type Labyrinth<K> = HashMap<Position, Tile<K>>;

trait HasNeighbors<K> {
  fn neighbors(&self, labyrinth: &Labyrinth<K>, keys: &[K]) -> Vec<Self> where Self: Sized;
}

impl<K> HasNeighbors<K> for Position
where K: Hash + Eq {
  fn neighbors(&self, labyrinth: &Labyrinth<K>, keys: &[K]) -> Vec<Position> {
    let &(x, y) = self;

    [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
    .cloned()
    .filter(|&(x, y)| x > 0 && y > 0)
    .filter(|pos| match labyrinth.get(pos) {
      Some(Tile::Passage) | Some(Tile::Key(_)) => true,
      Some(Tile::Door(x)) => keys.contains(x),
      _ => false
    })
    .collect()
  }
}

impl<K> HasNeighbors<K> for Vec<Position>
where K: Hash + Eq + Clone {
  fn neighbors(&self, labyrinth: &Labyrinth<K>, keys: &[K]) -> Vec<Vec<Position>> {
    self.iter()
    .cloned()
    .enumerate()
    .flat_map(|(i, position)| {
      position.neighbors(labyrinth, keys).into_iter()
      .map(move |neighbor| {
        let mut positions = self.iter().cloned().collect::<Vec<_>>();
        positions[i] = neighbor;
        positions
      })
    })
    .collect()
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("18.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_labyrinth(input: &str) -> (Labyrinth<char>, Option<Position>) {
  let labyrinth = input.lines()
    .enumerate()
    .flat_map(|(y, line)| {
      line.chars()
      .enumerate()
      .map(move |(x, c)| (
        (x, y),
        match c {
          '#' => Tile::Wall,
          '@' => Tile::Passage,
          c if c.is_ascii_uppercase() => Tile::Door(c.to_ascii_lowercase()),
          c if c.is_ascii_lowercase() => Tile::Key(c),
          _ => Tile::Passage
        }
      ))
    })
    .collect::<Labyrinth<_>>();

  let entrance = input.lines()
    .enumerate()
    .find_map(|(y, line)| {
      line.chars()
      .position(|c| c == '@')
      .map(|x| (x, y))
    });

  (labyrinth, entrance)
}

fn get_all_keys<K>(labyrinth: &Labyrinth<K>, position: Position, key_count: usize) -> Option<Vec<Position>>
where K: Hash + Eq + Clone + Ord {
  let mut queue = VecDeque::new();
  let mut previous_map = HashMap::new();
  let mut target = None;

  queue.push_back((position, vec![]));
  previous_map.insert((position, vec![]), None);

  while let Some((position, keys)) = queue.pop_front() {
    if keys.len() >= key_count {
      target = Some((position, keys));
      break;
    }

    for neighbor in position.neighbors(labyrinth, &keys) {
      let mut new_keys = keys.clone();

      if let Some(Tile::Key(c)) = labyrinth.get(&neighbor) {
        new_keys.push(c.clone());
        new_keys.sort();
        new_keys.dedup();
      }

      let map_key = (neighbor, new_keys);

      if !previous_map.contains_key(&map_key) {
        queue.push_back((neighbor, map_key.1.clone()));
        previous_map.insert(map_key, Some((position, keys.clone())));
      }
    }
  }

  target.map(|target| {
    let mut result = vec![target];

    while let Some(Some(prev)) = previous_map.get(&result.last().unwrap()) {
      result.push(prev.clone());
    }

    result.into_iter().rev().map(|(position, _)| position).collect()
  })
}

fn main() {
  let input = get_input().unwrap();
  let (mut labyrinth, position) = parse_labyrinth(&input);

  let path = get_all_keys(&labyrinth, position.unwrap(), 26);
  println!("Part 1: {}", path.unwrap().len() - 1);

  let (x, y) = position.unwrap();
  labyrinth.insert((x, y), Tile::Wall);
  labyrinth.insert((x - 1, y), Tile::Wall);
  labyrinth.insert((x + 1, y), Tile::Wall);
  labyrinth.insert((x, y - 1), Tile::Wall);
  labyrinth.insert((x, y + 1), Tile::Wall);

  let positions = vec![(x - 1, y - 1), (x + 1, y - 1), (x - 1, y + 1), (x + 1, y + 1)];
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate fittings_server;

use fittings_server::examples;

fn main() {
    let fittings_rocket = rocket::ignite();

    let mut routes = examples::get_routes("/");
    routes.extend(routes![index, get_fav_icon, get_static_files]);

    fittings_rocket.mount("/", routes).launch();
}

//ZZZ TODO Move static files out
use std::io;
use std::path::{Path, PathBuf};
use rocket::response::NamedFile;

#[get("/<file..>")]
fn get_static_files(file: PathBuf) -> Option<NamedFile> {
    NamedFile::open(Path::new("static/").join(file)).ok()
}

#[get("/favicon.ico")]
fn get_fav_icon() -> Option<NamedFile> {
    NamedFile::open(Path::new("static/favicon.png")).ok()
}

// #[get("/main.css")]
// fn get_main_css() -> io::Result<NamedFile> {
//     NamedFile::open("static/main.css")
// }
//
#[get("/")]
fn index() -> io::Result<NamedFile> {
    NamedFile::open("static/index.html")
}
<commit_msg>Fixed icon<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate fittings_server;

use fittings_server::examples;

fn main() {
    let fittings_rocket = rocket::ignite();

    let mut routes = examples::get_routes("/");
    routes.extend(routes![index, get_fav_icon, get_static_files]);

    fittings_rocket.mount("/", routes).launch();
}

//ZZZ TODO Move static files out
use std::io;
use std::path::{Path, PathBuf};
use rocket::response::NamedFile;

#[get("/<file..>")]
fn get_static_files(file: PathBuf) -> Option<NamedFile> {
    NamedFile::open(Path::new("static/").join(file)).ok()
}

#[get("/favicon.ico")]
fn get_fav_icon() -> Option<NamedFile> {
    NamedFile::open(Path::new("static/favicon.ico")).ok()
}

// #[get("/main.css")]
// fn get_main_css() -> io::Result<NamedFile> {
//     NamedFile::open("static/main.css")
// }
//
#[get("/")]
fn index() -> io::Result<NamedFile> {
    NamedFile::open("static/index.html")
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::rc::{Rc, Weak};
use std::cmp;
use std::mem;
use uuid::Uuid;

use alloc::{Alloc, AllocBox};
use js_types::js_type::{JsVar, JsType, JsPtrEnum};

pub struct Scope {
    pub parent: Option<Rc<Scope>>,
    alloc_box: Rc<RefCell<AllocBox>>,
    get_roots: Box<Fn() -> HashSet<Uuid>>,
}

impl Scope {
    pub fn new<F>(alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: None,
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn as_child<F>(parent: &Rc<Scope>, alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: Some(parent.clone()),
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn set_parent(&mut self, parent: &Rc<Scope>) {
        self.parent = Some(parent.clone());
    }

    pub fn alloc(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> Uuid {
        self.alloc_box.borrow_mut().alloc(uuid, ptr)
    }

    pub fn dealloc(&mut self, uuid: &Uuid) -> bool {
        self.alloc_box.borrow_mut().dealloc(uuid)
    }

    pub fn get_ptr_copy(&self, uuid: &Uuid) -> Option<JsPtrEnum> {
        self.alloc_box.borrow().find_id(uuid).map(|var| var.borrow().clone())
    }

    pub fn update_ptr(&mut self, uuid: &Uuid, ptr: JsPtrEnum) -> bool {
        self.alloc_box.borrow_mut().update_var(uuid, ptr)
    }

    /// TODO Compute the roots of the current scope-- any variable that is
    /// directly referenced or declared within the scope. This might just be the
    /// key set of the uuid map(?) Not necessarily, I think. What if you do
    /// something like this:
    /// var x = {}
    /// var y = { 1: x }
    /// y = x
    /// y would be a root by the definition above, but is no longer reachable at
    /// the end of the scope because it now aliases x. A better definition would
    /// be "Any variable that is declared or referenced directly, but a direct
    /// reference (variable usage) supercedes a declaration." The above example
    /// demonstrates why this is necessary.
    /// This should come from the interpreter, so I shouldn't actually have to
    /// care about getting the root set myself.

    /// Roots always get marked as Black, since they're always reachable from
    /// the current scope. NB that this assumes all root references are actually
    /// valid reference types, i.e. they're not numbers, etc.
    pub fn mark_roots(&mut self) {
        let marks = (self.get_roots)();
        self.alloc_box.borrow_mut().mark_roots(marks);
    }

}

#[cfg(test)]
mod tests {
    /*use super::*;
    use std::collections::hash_set::HashSet;
    use std::rc::{Rc, Weak};
    use uuid::Uuid;

    use alloc::Alloc;
    use js_types::js_type::{JsVar, JsType, JsPtrEnum, JsKey, JsKeyEnum};
    use js_types::js_obj::{JsObjStruct};

    fn dummy_get_roots() -> HashSet<Uuid> {
        HashSet::new()
    }

    fn make_num(i: f64) -> JsVar {
        JsVar::new(JsType::JsNum(i))
    }

    fn make_obj(kvs: Vec<(JsKey, Alloc<JsVar>)>) -> JsVar {
        JsVar::new(JsType::JsPtr(JsPtrEnum::JsObj(JsObjStruct::new(None, "test", kvs))))
    }

    #[test]
    fn test_new_scope() {
        let mut test_scope = Scope::new(dummy_get_roots);
        assert!(test_scope.parent.is_none());
        assert!(test_scope.black_set.is_empty());
        assert!(test_scope.grey_set.is_empty());
        assert!(test_scope.white_set.is_empty());
        assert_eq!(test_scope.children.len(), 0);
    }

    #[test]
    fn test_as_child_scope() {
        let parent_scope = Rc::new(Scope::new(dummy_get_roots));
        let mut test_scope = Scope::as_child(Rc::downgrade(&parent_scope.clone()), dummy_get_roots);

        assert!(test_scope.parent.is_some());
        assert!(test_scope.black_set.is_empty());
        assert!(test_scope.grey_set.is_empty());
        assert!(test_scope.white_set.is_empty());
        assert_eq!(test_scope.children.len(), 0);
    }

    #[test]
    fn test_set_parent() {
        let parent_scope = Rc::new(Scope::new(dummy_get_roots));
        let mut test_scope = Scope::new(dummy_get_roots);
        assert!(test_scope.parent.is_none());
        test_scope.set_parent(&parent_scope);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_add_child() {
        let mut test_scope = Scope::new(dummy_get_roots);
        let child_scope1 = Scope::new(dummy_get_roots);
        let child_scope2 = Scope::new(dummy_get_roots);
        assert_eq!(test_scope.children.len(), 0);
        test_scope.add_child(child_scope1);
        assert_eq!(test_scope.children.len(), 1);
        test_scope.add_child(child_scope2);
        assert_eq!(test_scope.children.len(), 2);
    }

    #[test]
    fn test_alloc() {
        let mut test_scope = Scope::new(dummy_get_roots);
        let test_var = make_num(1.0);
        let test_uuid = test_var.uuid.clone();
        let uuid = test_scope.alloc(test_var);
        assert_eq!(test_uuid, uuid);
        assert!(test_scope.white_set.contains_key(&uuid));
        assert_eq!(test_scope.white_set.len(), 1);
        assert_eq!(test_scope.grey_set.len(), 0);
        assert_eq!(test_scope.black_set.len(), 0);
        let test_var2 = make_num(2.0);
        let uuid = test_scope.alloc(test_var2);
        assert!(test_scope.white_set.contains_key(&uuid));
        assert_eq!(test_scope.white_set.len(), 2);
        assert_eq!(test_scope.grey_set.len(), 0);
        assert_eq!(test_scope.black_set.len(), 0);
    }

    #[test]
    fn test_dealloc() {
        let mut test_scope = Scope::new(dummy_get_roots);
        let test_var = make_num(1.0);
        let uuid = test_scope.alloc(test_var);
        let bad_uuid = Uuid::new_v4();
        assert!(test_scope.dealloc(&uuid));
        assert_eq!(test_scope.white_set.len(), 0);
        assert_eq!(test_scope.grey_set.len(), 0);
        assert_eq!(test_scope.black_set.len(), 0);
        assert!(!test_scope.dealloc(&bad_uuid));
    }

    #[test]
    fn test_get_var_copy() {
        let mut test_scope = Scope::new(dummy_get_roots);
        let test_var = make_num(1.0);
        let uuid = test_scope.alloc(test_var);
        let bad_uuid = Uuid::new_v4();
        let var_copy = test_scope.get_var_copy(&uuid);
        assert!(var_copy.is_some());
        let var = var_copy.unwrap();
        assert_eq!(var.uuid, uuid);
        let bad_copy = test_scope.get_var_copy(&bad_uuid);
        assert!(bad_copy.is_none());
    }

    #[test]
    fn test_update_var() {
        let mut test_scope = Scope::new(dummy_get_roots);
        let test_var = make_num(1.0);
        let uuid = test_scope.alloc(test_var);
        let mut update = test_scope.get_var_copy(&uuid).unwrap();
        update = make_num(2.0);
        update.uuid = uuid;
        assert!(test_scope.update_var(update));
        let update = test_scope.get_var_copy(&uuid).unwrap();
        match update {
            JsVar{ t: JsType::JsNum(i), ..} => assert_eq!(i, 2.0),
            _ => ()
        }
        test_scope.dealloc(&uuid);
        assert!(!test_scope.update_var(update));
    }

    #[test]
    fn test_mark_roots() {
        let test_uuid = Uuid::new_v4();
        let mut test_var = make_num(1.0);
        let mut test_var2 = make_num(2.0);
        test_var.uuid = test_uuid;
        let simple_get_roots = move || { let mut set = HashSet::new();
                                    set.insert(test_uuid);
                                    set };
        let mut test_scope = Scope::new(simple_get_roots);
        let test_uuid = test_scope.alloc(test_var);
        let test_uuid2 = test_scope.alloc(test_var2);
        test_scope.mark_roots();
        assert_eq!(test_scope.black_set.len(), 1);
        assert!(test_scope.black_set.get(&test_uuid).is_some());
        assert!(test_scope.black_set.get(&test_uuid2).is_none());
    }

    #[test]
    fn test_mark_phase() {
        let test_uuid = Uuid::new_v4();
        let simple_get_roots = move || { let mut set = HashSet::new();
                                    set.insert(test_uuid);
                                    set };
        let mut test_scope = Scope::new(simple_get_roots);

        let mut test_var2 = make_num(2.0);
        let test_uuid2 = test_scope.alloc(test_var2);
        let test_var2_alloc = test_scope.find_id(&test_uuid2).unwrap().clone();

        let test_key = JsKey::new(JsKeyEnum::JsNum(1.0));
        let mut test_var = make_obj(vec![(test_key, test_var2_alloc)]);
        test_var.uuid = test_uuid;
        let test_uuid = test_scope.alloc(test_var);

        // Mark roots first...
        test_scope.mark_roots();
        assert!(test_scope.black_set.get(&test_uuid).is_some());
        assert_eq!(test_scope.black_set.len(), 1);
        assert_eq!(test_scope.grey_set.len(), 1);

        // ...then mark everything else
        test_scope.mark_phase();

        assert_eq!(test_scope.grey_set.len(), 0);
        assert!(test_scope.black_set.get(&test_uuid).is_some());
        assert!(test_scope.black_set.get(&test_uuid2).is_some());
    }*/
}
<commit_msg>Fix Scope tests<commit_after>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::rc::{Rc, Weak};
use std::cmp;
use std::mem;
use uuid::Uuid;

use alloc::{Alloc, AllocBox};
use js_types::js_type::{JsVar, JsType, JsPtrEnum};

pub struct Scope {
    pub parent: Option<Rc<Scope>>,
    alloc_box: Rc<RefCell<AllocBox>>,
    get_roots: Box<Fn() -> HashSet<Uuid>>,
}

impl Scope {
    pub fn new<F>(alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: None,
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn as_child<F>(parent: &Rc<Scope>, alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: Some(parent.clone()),
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn set_parent(&mut self, parent: &Rc<Scope>) {
        self.parent = Some(parent.clone());
    }

    pub fn alloc(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> Uuid {
        self.alloc_box.borrow_mut().alloc(uuid, ptr)
    }

    pub fn dealloc(&mut self, uuid: &Uuid) -> bool {
        self.alloc_box.borrow_mut().dealloc(uuid)
    }

    pub fn get_ptr_copy(&self, uuid: &Uuid) -> Option<JsPtrEnum> {
        self.alloc_box.borrow().find_id(uuid).map(|var| var.borrow().clone())
    }

    pub fn update_ptr(&mut self, uuid: &Uuid, ptr: JsPtrEnum) -> bool {
        self.alloc_box.borrow_mut().update_var(uuid, ptr)
    }

    /// TODO Compute the roots of the current scope-- any variable that is
    /// directly referenced or declared within the scope. This might just be the
    /// key set of the uuid map(?) Not necessarily, I think. What if you do
    /// something like this:
    /// var x = {}
    /// var y = { 1: x }
    /// y = x
    /// y would be a root by the definition above, but is no longer reachable at
    /// the end of the scope because it now aliases x. A better definition would
    /// be "Any variable that is declared or referenced directly, but a direct
    /// reference (variable usage) supercedes a declaration." The above example
    /// demonstrates why this is necessary.
    /// This should come from the interpreter, so I shouldn't actually have to
    /// care about getting the root set myself.

    /// Roots always get marked as Black, since they're always reachable from
    /// the current scope. NB that this assumes all root references are actually
    /// valid reference types, i.e. they're not numbers, etc.
    pub fn mark_roots(&mut self) {
        let marks = (self.get_roots)();
        self.alloc_box.borrow_mut().mark_roots(marks);
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;
    use std::collections::hash_set::HashSet;
    use std::rc::{Rc, Weak};
    use uuid::Uuid;

    use alloc::{Alloc, AllocBox};
    use js_types::js_type::{JsVar, JsType, JsPtrEnum, JsKey, JsKeyEnum};
    use js_types::js_obj::JsObjStruct;
    use js_types::js_str::JsStrStruct;

    fn dummy_get_roots() -> HashSet<Uuid> {
        HashSet::new()
    }

    fn make_alloc_box() -> Rc<RefCell<AllocBox>> {
        Rc::new(RefCell::new(AllocBox::new()))
    }

    fn make_num(i: f64) -> JsVar {
        JsVar::new(JsType::JsNum(i))
    }

    fn make_obj(alloc_box: Rc<RefCell<AllocBox>>, kvs: Vec<(JsKey, JsVar)>) -> JsVar {
        let var = JsVar::new(JsType::JsPtr);
        alloc_box.borrow_mut()
            .alloc(var.uuid, JsPtrEnum::JsObj(JsObjStruct::new(None, "test", kvs)));
        var
    }

    #[test]
    fn test_new_scope() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_none());
    }

    #[test]
    fn test_as_child_scope() {
        let alloc_box = make_alloc_box();
        let parent_scope = Rc::new(Scope::new(&alloc_box, dummy_get_roots));
        let mut test_scope = Scope::as_child(&parent_scope, &alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_set_parent() {
        let alloc_box = make_alloc_box();
        let parent_scope = Rc::new(Scope::new(&alloc_box, dummy_get_roots));
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_none());
        test_scope.set_parent(&parent_scope);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_alloc() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        assert_eq!(test_id, test_var.uuid);
    }

    #[test]
    fn test_dealloc() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        let bad_uuid = Uuid::new_v4();
        assert!(test_scope.dealloc(&test_id));
        assert!(!test_scope.dealloc(&bad_uuid));
    }

    #[test]
    fn test_get_ptr_copy() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        let bad_uuid = Uuid::new_v4();

        let ptr_copy = test_scope.get_ptr_copy(&test_id);
        assert!(ptr_copy.is_some());

        // FIXME it's kind of problematic that UUIDs are separated
        // from ptr types
        //let ptr = ptr_copy.unwrap();
        //assert_eq!(ptr.uuid, test_id);

        let bad_copy = test_scope.get_ptr_copy(&bad_uuid);
        assert!(bad_copy.is_none());
    }

    #[test]
    fn test_update_ptr() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        let mut update = test_scope.get_ptr_copy(&test_id).unwrap();
        update = JsPtrEnum::JsStr(JsStrStruct::new("test"));
        assert!(test_scope.update_ptr(&test_id, update));

        let update = test_scope.get_ptr_copy(&test_id).unwrap();
        match update {
            JsPtrEnum::JsStr(JsStrStruct{text: ref s}) => assert_eq!(s, "test"),
            _ => ()
        }
        test_scope.dealloc(&test_id);
        assert!(!test_scope.update_ptr(&test_id, update));
    }
}
<|endoftext|>


<commit_before><commit_msg>Added needed inline flags<commit_after><|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

use builder::BuildResult;

static TABLE_NAME : &'static str = "tbl";

/// Type A is some key
pub trait Database<A> : Sync + Send {
    fn add_pending(&self, entry: A);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<A>;

    fn add_test_results(&self, entry: A, results: BuildResult);

    fn results_for_entry(&self, entry: A) -> Option<String>;
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    fn to_int(&self) -> int {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

// TODO: we are abstracting over SQLite3 bindings, which do not
// provide proper parameter passing and are thus susceptible to
// injection attacks.  We should make it so we only care about
// Postgres.
pub trait SqlDatabaseInterface {
    /// Executes the given query, and returns the number of rows modified
    /// as a result.
    fn execute_query(&self, query: &str) -> uint;

    /// Reads a textual column from a query which is expected to
    /// return one text column, and at most one row.  Returns None if
    /// there were no rows returned.
    fn read_one_string(&self, query: &str) -> Option<String>;
}

mod sql_db_helpers {
    use super::{SqlDatabaseInterface, TABLE_NAME, Pending, InProgress};

    pub fn get_candidate_entry<T : SqlDatabaseInterface>(t: &T) -> Option<String> {
        t.read_one_string(
            format!("SELECT entry FROM {} WHERE status = {} LIMIT 1",
                    TABLE_NAME, Pending.to_int()).as_slice())
    }

    pub fn try_lock_entry<T : SqlDatabaseInterface>(t: &T, entry: &String) -> bool {
        let num_changed =
            t.execute_query(
                format!("UPDATE {} SET status = {} WHERE entry = \"{}\" AND status = {}",
                        TABLE_NAME,
                        InProgress.to_int(),
                        entry,
                        Pending.to_int()).as_slice());
        match num_changed {
            0 => false,
            1 => true,
            _ => { assert!(false); false }
        }
    }
}

impl<T : SqlDatabaseInterface + Send + Sync> Database<String> for T {
    fn add_pending(&self, entry: String) {
        self.execute_query(
            format!("INSERT INTO {} VALUES (\"{}\", {}, NULL)",
                    TABLE_NAME, entry, Pending.to_int()).as_slice());
    }

    fn get_pending(&self) -> Option<String> {
        loop {
            match sql_db_helpers::get_candidate_entry(self) {
                Some(entry) => {
                    if sql_db_helpers::try_lock_entry(self, &entry) {
                        return Some(entry);
                    }
                }
                None => { return None; }
            }
        }
    }

    fn add_test_results(&self, entry: String, results: BuildResult) {
        let num_changed =
            self.execute_query(
                format!("UPDATE {} SET status = {}, results = \"{}\" WHERE entry = \"{}\"",
                        TABLE_NAME,
                        Done.to_int(),
                        results.to_string(),
                        entry).as_slice());
        assert_eq!(num_changed, 1);
    }

    fn results_for_entry(&self, entry: String) -> Option<String> {
        self.read_one_string(
            format!("SELECT results FROM {} WHERE entry = \"{}\"",
                    TABLE_NAME, entry).as_slice())
    }

}

/// For integration tests.
pub mod testing {
    use std::collections::HashMap;
    use std::sync::mpmc_bounded_queue::Queue;
    use std::sync::RWLock;

    use builder::BuildResult;
    use super::Database;

    /// Simply a directory to a status.
    pub struct TestDatabase {
        pending: Queue<String>,
        results: RWLock<HashMap<String, BuildResult>>,
    }

    impl TestDatabase {
        pub fn new() -> TestDatabase {
            TestDatabase {
                pending: Queue::with_capacity(10),
                results: RWLock::new(HashMap::new())
            }
        }
    }

    impl Database<String> for TestDatabase {
        fn add_pending(&self, entry: String) {
            self.pending.push(entry);
        }

        fn get_pending(&self) -> Option<String> {
            self.pending.pop()
        }

        fn add_test_results(&self, entry: String, results: BuildResult) {
            let mut val = self.results.write();
            val.insert(entry, results);
            val.downgrade();
        }

        fn results_for_entry(&self, entry: String) -> Option<String> {
            self.results.read().find_equiv(&entry).map(|res| res.to_string())
        }
    }
}

pub mod sqlite {
    extern crate sqlite3;

    use std::sync::Mutex;

    use self::sqlite3::types::SqliteResult;
    use self::sqlite3::types::{SQLITE_ROW, SQLITE_DONE};
    use self::sqlite3::database::Database as SqliteDatabaseInternal;

    use super::SqlDatabaseInterface;

    pub struct SqliteDatabase {
        db: Mutex<SqliteDatabaseInternal>
    }

    impl SqliteDatabase {
        pub fn new() -> SqliteResult<SqliteDatabase> {
            let mut db = try!(sqlite3::open(":memory:"));

            try!(db.exec(
                "CREATE TABLE tbl(entry TEXT PRIMARY KEY, status INTEGER NOT NULL, results Text)"));
            Ok(SqliteDatabase { db: Mutex::new(db) })
        }
    }

    impl SqlDatabaseInterface for SqliteDatabase {
        fn execute_query(&self, query: &str) -> uint {
            let mut lock = self.db.lock();
            lock.exec(query).ok().expect(query);
            lock.get_changes().to_uint().unwrap()
        }

        fn read_one_string(&self, query: &str) -> Option<String> {
            let lock = self.db.lock();
            let mut cursor = lock.prepare(query, &None).ok().expect("TWO");
            let step_one = cursor.step();
            if step_one == SQLITE_ROW {
                let op_text = cursor.get_text(0).map(|s| s.to_string());
                let step_two = cursor.step();
                assert_eq!(step_two, SQLITE_DONE);
                op_text
            } else {
                None
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::Database;
    use super::sqlite::SqliteDatabase;
    use super::testing::TestDatabase;

    use builder::TestSuccess;

    use std::collections::HashMap;

    static KEY : &'static str = "foobar";

    fn add_get_pending<D : Database<String>>(db: &D) {
        db.add_pending(KEY.to_string());
        assert_eq!(db.get_pending(), Some(KEY.to_string()));
    }

    fn add_test_results<D : Database<String>>(db: &D) {
        add_get_pending(db);
        db.add_test_results(KEY.to_string(),
                            TestSuccess(HashMap::new()));
    }

    #[test]
    fn memory_add_get_pending() {
        add_get_pending(&TestDatabase::new());
    }

    #[test]
    fn memory_add_test_results() {
        add_test_results(&TestDatabase::new());
    }

    #[test]
    fn sqlite_add_get_pending() {
        add_get_pending(&SqliteDatabase::new().unwrap());
    }

    #[test]
    fn sqlite_add_test_results() {
        add_test_results(&SqliteDatabase::new().unwrap());
    }
}
<commit_msg>Fixed a missed parameterization of the table name.<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

use builder::BuildResult;

static TABLE_NAME : &'static str = "tbl";

/// Type A is some key
pub trait Database<A> : Sync + Send {
    fn add_pending(&self, entry: A);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<A>;

    fn add_test_results(&self, entry: A, results: BuildResult);

    fn results_for_entry(&self, entry: A) -> Option<String>;
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    fn to_int(&self) -> int {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

// TODO: we are abstracting over SQLite3 bindings, which do not
// provide proper parameter passing and are thus susceptible to
// injection attacks.  We should make it so we only care about
// Postgres.
pub trait SqlDatabaseInterface {
    /// Executes the given query, and returns the number of rows modified
    /// as a result.
    fn execute_query(&self, query: &str) -> uint;

    /// Reads a textual column from a query which is expected to
    /// return one text column, and at most one row.  Returns None if
    /// there were no rows returned.
    fn read_one_string(&self, query: &str) -> Option<String>;
}

mod sql_db_helpers {
    use super::{SqlDatabaseInterface, TABLE_NAME, Pending, InProgress};

    pub fn get_candidate_entry<T : SqlDatabaseInterface>(t: &T) -> Option<String> {
        t.read_one_string(
            format!("SELECT entry FROM {} WHERE status = {} LIMIT 1",
                    TABLE_NAME, Pending.to_int()).as_slice())
    }

    pub fn try_lock_entry<T : SqlDatabaseInterface>(t: &T, entry: &String) -> bool {
        let num_changed =
            t.execute_query(
                format!("UPDATE {} SET status = {} WHERE entry = \"{}\" AND status = {}",
                        TABLE_NAME,
                        InProgress.to_int(),
                        entry,
                        Pending.to_int()).as_slice());
        match num_changed {
            0 => false,
            1 => true,
            _ => { assert!(false); false }
        }
    }
}

impl<T : SqlDatabaseInterface + Send + Sync> Database<String> for T {
    fn add_pending(&self, entry: String) {
        self.execute_query(
            format!("INSERT INTO {} VALUES (\"{}\", {}, NULL)",
                    TABLE_NAME, entry, Pending.to_int()).as_slice());
    }

    fn get_pending(&self) -> Option<String> {
        loop {
            match sql_db_helpers::get_candidate_entry(self) {
                Some(entry) => {
                    if sql_db_helpers::try_lock_entry(self, &entry) {
                        return Some(entry);
                    }
                }
                None => { return None; }
            }
        }
    }

    fn add_test_results(&self, entry: String, results: BuildResult) {
        let num_changed =
            self.execute_query(
                format!("UPDATE {} SET status = {}, results = \"{}\" WHERE entry = \"{}\"",
                        TABLE_NAME,
                        Done.to_int(),
                        results.to_string(),
                        entry).as_slice());
        assert_eq!(num_changed, 1);
    }

    fn results_for_entry(&self, entry: String) -> Option<String> {
        self.read_one_string(
            format!("SELECT results FROM {} WHERE entry = \"{}\"",
                    TABLE_NAME, entry).as_slice())
    }

}

/// For integration tests.
pub mod testing {
    use std::collections::HashMap;
    use std::sync::mpmc_bounded_queue::Queue;
    use std::sync::RWLock;

    use builder::BuildResult;
    use super::Database;

    /// Simply a directory to a status.
    pub struct TestDatabase {
        pending: Queue<String>,
        results: RWLock<HashMap<String, BuildResult>>,
    }

    impl TestDatabase {
        pub fn new() -> TestDatabase {
            TestDatabase {
                pending: Queue::with_capacity(10),
                results: RWLock::new(HashMap::new())
            }
        }
    }

    impl Database<String> for TestDatabase {
        fn add_pending(&self, entry: String) {
            self.pending.push(entry);
        }

        fn get_pending(&self) -> Option<String> {
            self.pending.pop()
        }

        fn add_test_results(&self, entry: String, results: BuildResult) {
            let mut val = self.results.write();
            val.insert(entry, results);
            val.downgrade();
        }

        fn results_for_entry(&self, entry: String) -> Option<String> {
            self.results.read().find_equiv(&entry).map(|res| res.to_string())
        }
    }
}

pub mod sqlite {
    extern crate sqlite3;

    use std::sync::Mutex;

    use self::sqlite3::types::SqliteResult;
    use self::sqlite3::types::{SQLITE_ROW, SQLITE_DONE};
    use self::sqlite3::database::Database as SqliteDatabaseInternal;

    use super::{SqlDatabaseInterface, TABLE_NAME};

    pub struct SqliteDatabase {
        db: Mutex<SqliteDatabaseInternal>
    }

    impl SqliteDatabase {
        pub fn new() -> SqliteResult<SqliteDatabase> {
            let mut db = try!(sqlite3::open(":memory:"));

            try!(
                db.exec(
                    format!(
                        "CREATE TABLE {}(entry TEXT PRIMARY KEY, status INTEGER NOT NULL, results Text)",
                        TABLE_NAME).as_slice()));
            Ok(SqliteDatabase { db: Mutex::new(db) })
        }
    }

    impl SqlDatabaseInterface for SqliteDatabase {
        fn execute_query(&self, query: &str) -> uint {
            let mut lock = self.db.lock();
            lock.exec(query).ok().expect(query);
            lock.get_changes().to_uint().unwrap()
        }

        fn read_one_string(&self, query: &str) -> Option<String> {
            let lock = self.db.lock();
            let mut cursor = lock.prepare(query, &None).ok().expect("TWO");
            let step_one = cursor.step();
            if step_one == SQLITE_ROW {
                let op_text = cursor.get_text(0).map(|s| s.to_string());
                let step_two = cursor.step();
                assert_eq!(step_two, SQLITE_DONE);
                op_text
            } else {
                None
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::Database;
    use super::sqlite::SqliteDatabase;
    use super::testing::TestDatabase;

    use builder::TestSuccess;

    use std::collections::HashMap;

    static KEY : &'static str = "foobar";

    fn add_get_pending<D : Database<String>>(db: &D) {
        db.add_pending(KEY.to_string());
        assert_eq!(db.get_pending(), Some(KEY.to_string()));
    }

    fn add_test_results<D : Database<String>>(db: &D) {
        add_get_pending(db);
        db.add_test_results(KEY.to_string(),
                            TestSuccess(HashMap::new()));
    }

    #[test]
    fn memory_add_get_pending() {
        add_get_pending(&TestDatabase::new());
    }

    #[test]
    fn memory_add_test_results() {
        add_test_results(&TestDatabase::new());
    }

    #[test]
    fn sqlite_add_get_pending() {
        add_get_pending(&SqliteDatabase::new().unwrap());
    }

    #[test]
    fn sqlite_add_test_results() {
        add_test_results(&SqliteDatabase::new().unwrap());
    }
}
<|endoftext|>


<commit_before>pub static HEADER_GRPC_STATUS: &'static str = "grpc-status";
pub static HEADER_GRPC_MESSAGE: &'static str = "grpc-message";

// copied from https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h
#[allow(dead_code)]
pub enum GrpcStatus {
    /* Not an error; returned on success */
    Ok = 0,

    /* The operation was cancelled (typically by the caller). */
    Cancelled = 1,

    /* Unknown error.  An example of where this error may be returned is
       if a Status value received from another address space belongs to
       an error-space that is not known in this address space.  Also
       errors raised by APIs that do not return enough error information
       may be converted to this error. */
    Unknown = 2,

    /* Client specified an invalid argument.  Note that this differs
       from FAILED_PRECONDITION.  INVALID_ARGUMENT indicates arguments
       that are problematic regardless of the state of the system
       (e.g., a malformed file name). */
    Argument = 3,

    /* Deadline expired before operation could complete.  For operations
       that change the state of the system, this error may be returned
       even if the operation has completed successfully.  For example, a
       successful response from a server could have been delayed long
       enough for the deadline to expire. */
    DeadlineExceeded = 4,

    /* Some requested entity (e.g., file or directory) was not found. */
    NotFound = 5,

    /* Some entity that we attempted to create (e.g., file or directory)
       already exists. */
    AlreadyExists = 6,

    /* The caller does not have permission to execute the specified
       operation.  PERMISSION_DENIED must not be used for rejections
       caused by exhausting some resource (use RESOURCE_EXHAUSTED
       instead for those errors).  PERMISSION_DENIED must not be
       used if the caller can not be identified (use UNAUTHENTICATED
       instead for those errors). */
    PermissionDenied = 7,

    /* The request does not have valid authentication credentials for the
       operation. */
    Unauthenticated = 16,

    /* Some resource has been exhausted, perhaps a per-user quota, or
       perhaps the entire file system is out of space. */
    ResourceExhausted = 8,

    /* Operation was rejected because the system is not in a state
       required for the operation's execution.  For example, directory
       to be deleted may be non-empty, an rmdir operation is applied to
       a non-directory, etc.
       A litmus test that may help a service implementor in deciding
       between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
        (a) Use UNAVAILABLE if the client can retry just the failing call.
        (b) Use ABORTED if the client should retry at a higher-level
            (e.g., restarting a read-modify-write sequence).
        (c) Use FAILED_PRECONDITION if the client should not retry until
            the system state has been explicitly fixed.  E.g., if an "rmdir"
            fails because the directory is non-empty, FAILED_PRECONDITION
            should be returned since the client should not retry unless
            they have first fixed up the directory by deleting files from it.
        (d) Use FAILED_PRECONDITION if the client performs conditional
            REST Get/Update/Delete on a resource and the resource on the
            server does not match the condition. E.g., conflicting
            read-modify-write on the same resource. */
    FailedPrecondition = 9,

    /* The operation was aborted, typically due to a concurrency issue
       like sequencer check failures, transaction aborts, etc.
       See litmus test above for deciding between FAILED_PRECONDITION,
       ABORTED, and UNAVAILABLE. */
    Aborted = 10,

    /* Operation was attempted past the valid range.  E.g., seeking or
       reading past end of file.
       Unlike INVALID_ARGUMENT, this error indicates a problem that may
       be fixed if the system state changes. For example, a 32-bit file
       system will generate INVALID_ARGUMENT if asked to read at an
       offset that is not in the range [0,2^32-1], but it will generate
       OUT_OF_RANGE if asked to read from an offset past the current
       file size.
       There is a fair bit of overlap between FAILED_PRECONDITION and
       OUT_OF_RANGE.  We recommend using OUT_OF_RANGE (the more specific
       error) when it applies so that callers who are iterating through
       a space can easily look for an OUT_OF_RANGE error to detect when
       they are done. */
    OutOfRange = 11,

    /* Operation is not implemented or not supported/enabled in this service. */
    Unimplemented = 12,

    /* Internal errors.  Means some invariants expected by underlying
       system has been broken.  If you see one of these errors,
       something is very broken. */
    Internal = 13,

    /* The service is currently unavailable.  This is a most likely a
       transient condition and may be corrected by retrying with
       a backoff.
       See litmus test above for deciding between FAILED_PRECONDITION,
       ABORTED, and UNAVAILABLE. */
    Unavailable = 14,

    /* Unrecoverable data loss or corruption. */
    DataLoss = 15,
}
<commit_msg>GrpcStatus converters from u32<commit_after>pub static HEADER_GRPC_STATUS: &'static str = "grpc-status";
pub static HEADER_GRPC_MESSAGE: &'static str = "grpc-message";

// copied from https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
pub enum GrpcStatus {
    /* Not an error; returned on success */
    Ok = 0,

    /* The operation was cancelled (typically by the caller). */
    Cancelled = 1,

    /* Unknown error.  An example of where this error may be returned is
       if a Status value received from another address space belongs to
       an error-space that is not known in this address space.  Also
       errors raised by APIs that do not return enough error information
       may be converted to this error. */
    Unknown = 2,

    /* Client specified an invalid argument.  Note that this differs
       from FAILED_PRECONDITION.  INVALID_ARGUMENT indicates arguments
       that are problematic regardless of the state of the system
       (e.g., a malformed file name). */
    Argument = 3,

    /* Deadline expired before operation could complete.  For operations
       that change the state of the system, this error may be returned
       even if the operation has completed successfully.  For example, a
       successful response from a server could have been delayed long
       enough for the deadline to expire. */
    DeadlineExceeded = 4,

    /* Some requested entity (e.g., file or directory) was not found. */
    NotFound = 5,

    /* Some entity that we attempted to create (e.g., file or directory)
       already exists. */
    AlreadyExists = 6,

    /* The caller does not have permission to execute the specified
       operation.  PERMISSION_DENIED must not be used for rejections
       caused by exhausting some resource (use RESOURCE_EXHAUSTED
       instead for those errors).  PERMISSION_DENIED must not be
       used if the caller can not be identified (use UNAUTHENTICATED
       instead for those errors). */
    PermissionDenied = 7,

    /* The request does not have valid authentication credentials for the
       operation. */
    Unauthenticated = 16,

    /* Some resource has been exhausted, perhaps a per-user quota, or
       perhaps the entire file system is out of space. */
    ResourceExhausted = 8,

    /* Operation was rejected because the system is not in a state
       required for the operation's execution.  For example, directory
       to be deleted may be non-empty, an rmdir operation is applied to
       a non-directory, etc.
       A litmus test that may help a service implementor in deciding
       between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
        (a) Use UNAVAILABLE if the client can retry just the failing call.
        (b) Use ABORTED if the client should retry at a higher-level
            (e.g., restarting a read-modify-write sequence).
        (c) Use FAILED_PRECONDITION if the client should not retry until
            the system state has been explicitly fixed.  E.g., if an "rmdir"
            fails because the directory is non-empty, FAILED_PRECONDITION
            should be returned since the client should not retry unless
            they have first fixed up the directory by deleting files from it.
        (d) Use FAILED_PRECONDITION if the client performs conditional
            REST Get/Update/Delete on a resource and the resource on the
            server does not match the condition. E.g., conflicting
            read-modify-write on the same resource. */
    FailedPrecondition = 9,

    /* The operation was aborted, typically due to a concurrency issue
       like sequencer check failures, transaction aborts, etc.
       See litmus test above for deciding between FAILED_PRECONDITION,
       ABORTED, and UNAVAILABLE. */
    Aborted = 10,

    /* Operation was attempted past the valid range.  E.g., seeking or
       reading past end of file.
       Unlike INVALID_ARGUMENT, this error indicates a problem that may
       be fixed if the system state changes. For example, a 32-bit file
       system will generate INVALID_ARGUMENT if asked to read at an
       offset that is not in the range [0,2^32-1], but it will generate
       OUT_OF_RANGE if asked to read from an offset past the current
       file size.
       There is a fair bit of overlap between FAILED_PRECONDITION and
       OUT_OF_RANGE.  We recommend using OUT_OF_RANGE (the more specific
       error) when it applies so that callers who are iterating through
       a space can easily look for an OUT_OF_RANGE error to detect when
       they are done. */
    OutOfRange = 11,

    /* Operation is not implemented or not supported/enabled in this service. */
    Unimplemented = 12,

    /* Internal errors.  Means some invariants expected by underlying
       system has been broken.  If you see one of these errors,
       something is very broken. */
    Internal = 13,

    /* The service is currently unavailable.  This is a most likely a
       transient condition and may be corrected by retrying with
       a backoff.
       See litmus test above for deciding between FAILED_PRECONDITION,
       ABORTED, and UNAVAILABLE. */
    Unavailable = 14,

    /* Unrecoverable data loss or corruption. */
    DataLoss = 15,
}

const ALL_STATUSES: &[GrpcStatus] = &[
    GrpcStatus::Ok,
    GrpcStatus::Cancelled,
    GrpcStatus::Unknown,
    GrpcStatus::Argument,
    GrpcStatus::DeadlineExceeded,
    GrpcStatus::NotFound,
    GrpcStatus::AlreadyExists,
    GrpcStatus::PermissionDenied,
    GrpcStatus::Unauthenticated,
    GrpcStatus::ResourceExhausted,
    GrpcStatus::FailedPrecondition,
    GrpcStatus::Aborted,
    GrpcStatus::OutOfRange,
    GrpcStatus::Unimplemented,
    GrpcStatus::Internal,
    GrpcStatus::Unavailable,
    GrpcStatus::DataLoss,
];

impl GrpcStatus {
    /// GrpcStatus as protocol numeric code
    pub fn code(&self) -> u32 {
        *self as u32
    }

    /// Find GrpcStatus enum variant by code
    pub fn from_code(code: u32) -> Option<GrpcStatus> {
        ALL_STATUSES.into_iter().cloned().find(|s| s.code() == code)
    }

    /// Find GrpcStatus enum variant by code or return default value
    pub fn from_code_or_unknown(code: u32) -> GrpcStatus {
        GrpcStatus::from_code(code).unwrap_or(GrpcStatus::Unknown)
    }
}
<|endoftext|>


<commit_before>use clap::{App, Arg, AppSettings, Format, ArgMatches};
use std::io::{self, Write};
use std::process::exit;
use std::fs;
use crowbook::Book;

/// Prints an error on stderr and exit the program
pub fn print_error(s: &str) -> ! {
    writeln!(&mut io::stderr(), "{} {}", Format::Error("error:"), s).unwrap();
    exit(0);
}

/// sets the book options according to command line arguments
/// Also print these options to a string, so it can be used at
/// the creation of a book to check that parameters are OK and t
/// then print them to file
pub fn set_book_options(book: &mut Book, matches: &ArgMatches) -> String {
    let mut output = String::new();
    if let Some(iter) = matches.values_of("set") {
        let v:Vec<_> = iter.collect();
        if v.len() %2 != 0 {
            print_error("An odd number of arguments was passed to --set, but it takes a list of key value pairs.");
        }

        for i in 0..v.len()/2 {
            let key = v[i * 2];
            let value = v[i * 2 + 1];
            let res = book.options.set(key, value);
            if let Err(err) = res {
                print_error(&format!("Error in setting key {}: {}", key, err));
            }
            output.push_str(&format!("{}: {}\n", key, value));
        }
    }
    output
}

/// create a book file with the command line arguments
/// and exit the process at the end
pub fn create_book(matches: &ArgMatches) -> ! {
    let mut f:Box<Write> = if let Some(book) = matches.value_of("BOOK") {
        if fs::metadata(book).is_ok() {
            print_error(&format!("Could not create file {}: it already exists!", book));
        }
        Box::new(fs::File::create(book).unwrap())
    } else {
        Box::new(io::stdout())
    };

    if let Some(values) = matches.values_of("create") {
        if matches.is_present("set") {
            let mut book = Book::new();
            let s = set_book_options(&mut book, matches);
            f.write_all(&s.as_bytes()).unwrap();
        } else {
            f.write_all(b"author: Your name
title: Your title
lang: en

# Uncomment and fill to generate files
# output.html: some_file.html
# output.epub: some_file.epub
# output.pdf: some_file.pdf

# Uncomment and fill to set cover image (for Epub)
# cover: some_cover.png\n").unwrap();
        }
        f.write_all(b"\n# List of chapters\n").unwrap();
        for file in values {
            f.write_all(&format!("+ {}\n", file).as_bytes()).unwrap();
        }
        if let Some(s) = matches.value_of("BOOK") {
            println!("Created {}, now you'll have to complete it!", s);
        }
        exit(0);
    } else {
        print_error("--create must be used with a list of additonal files.

USAGE:
\tcrowbook [BOOK] --create <MARKDOWN_FILES>");
    }
}

pub fn create_matches<'a>() -> ArgMatches<'a> {
    let app = App::new("crowbook")
        .setting(AppSettings::UnifiedHelpMessage)
        .version(env!("CARGO_PKG_VERSION"))
        .about("Render a markdown book in Epub, PDF or HTML.")
        .arg_from_usage("-s, --single 'Create a book from a single MarkDown file'")
        .arg_from_usage("-v, --verbose 'Print warnings in parsing/rendering'")
        .arg_from_usage("-d, --debug 'Print additional information")
        .arg_from_usage("--create [FILES]... 'Creates a new book with existing markdown files.'")
        .arg(Arg::from_usage("-o, --output [FILE] 'Specifies output file.'")
             .requires("to"))
        .arg(Arg::from_usage("-t, --to [FORMAT] 'Generate specific format'")
             .possible_values(&["epub", "pdf", "html", "tex", "odt"]))
        .arg(Arg::from_usage("--set [KEY_VALUES] 'Sets a list of book options'")
             .min_values(2))
        .arg(Arg::from_usage("-l --list-options 'Lists all possible options"))
        .arg_from_usage("--list-options-md 'List all options, formatted in Markdown.'")
        .arg_from_usage("--print-template [TEMPLATE] 'Displays the default value of a template.'")
        .arg(Arg::with_name("BOOK")
             .index(1)
             .help("File containing the book configuration."));

    let matches = app.get_matches();

    pre_check(&matches);
    matches
}

/// Pre-check the matches to see if there isn't illegal options not detected by clap
fn pre_check(matches: &ArgMatches) {
    if matches.is_present("files") && !matches.is_present("create") {
        print_error("A list of additional files is only valid with the --create option.");
    }
}
<commit_msg>Improve the way --help is displayed<commit_after>use clap::{App, Arg, Format, ArgMatches};
use std::io::{self, Write};
use std::process::exit;
use std::fs;
use crowbook::Book;

/// Prints an error on stderr and exit the program
pub fn print_error(s: &str) -> ! {
    writeln!(&mut io::stderr(), "{} {}", Format::Error("error:"), s).unwrap();
    exit(0);
}

/// sets the book options according to command line arguments
/// Also print these options to a string, so it can be used at
/// the creation of a book to check that parameters are OK and t
/// then print them to file
pub fn set_book_options(book: &mut Book, matches: &ArgMatches) -> String {
    let mut output = String::new();
    if let Some(iter) = matches.values_of("set") {
        let v:Vec<_> = iter.collect();
        if v.len() %2 != 0 {
            print_error("An odd number of arguments was passed to --set, but it takes a list of key value pairs.");
        }

        for i in 0..v.len()/2 {
            let key = v[i * 2];
            let value = v[i * 2 + 1];
            let res = book.options.set(key, value);
            if let Err(err) = res {
                print_error(&format!("Error in setting key {}: {}", key, err));
            }
            output.push_str(&format!("{}: {}\n", key, value));
        }
    }
    output
}

/// create a book file with the command line arguments
/// and exit the process at the end
pub fn create_book(matches: &ArgMatches) -> ! {
    let mut f:Box<Write> = if let Some(book) = matches.value_of("BOOK") {
        if fs::metadata(book).is_ok() {
            print_error(&format!("Could not create file {}: it already exists!", book));
        }
        Box::new(fs::File::create(book).unwrap())
    } else {
        Box::new(io::stdout())
    };

    if let Some(values) = matches.values_of("create") {
        if matches.is_present("set") {
            let mut book = Book::new();
            let s = set_book_options(&mut book, matches);
            f.write_all(&s.as_bytes()).unwrap();
        } else {
            f.write_all(b"author: Your name
title: Your title
lang: en

# Uncomment and fill to generate files
# output.html: some_file.html
# output.epub: some_file.epub
# output.pdf: some_file.pdf

# Uncomment and fill to set cover image (for Epub)
# cover: some_cover.png\n").unwrap();
        }
        f.write_all(b"\n# List of chapters\n").unwrap();
        for file in values {
            f.write_all(&format!("+ {}\n", file).as_bytes()).unwrap();
        }
        if let Some(s) = matches.value_of("BOOK") {
            println!("Created {}, now you'll have to complete it!", s);
        }
        exit(0);
    } else {
        print_error("--create must be used with a list of additonal files.

USAGE:
\tcrowbook [BOOK] --create <MARKDOWN_FILES>");
    }
}

pub fn create_matches<'a>() -> ArgMatches<'a> {
    let app = App::new("crowbook")
        .version(env!("CARGO_PKG_VERSION"))
        .about("Render a markdown book in Epub, PDF or HTML.")
        .arg_from_usage("-s, --single 'Use a single Markdown file instead of a book configuration file'")
        .arg_from_usage("-v, --verbose 'Print warnings in parsing/rendering'")
        .arg_from_usage("-d, --debug 'Print debugging information'")
        .arg_from_usage("--create [FILES]... 'Creates a new book with existing markdown files.'")
        .arg(Arg::from_usage("-o, --output [FILE] 'Specifies output file.'")
             .requires("to"))
        .arg(Arg::from_usage("-t, --to [FORMAT] 'Generate specific format'")
             .possible_values(&["epub", "pdf", "html", "tex", "odt"]))
        .arg(Arg::from_usage("--set [KEY_VALUES] 'Sets a list of book options'")
             .min_values(2))
        .arg(Arg::from_usage("-l --list-options 'Lists all possible options"))
        .arg_from_usage("--list-options-md 'List all options, formatted in Markdown.'")
        .arg_from_usage("--print-template [TEMPLATE] 'Displays the default value of a template.'")
        .arg(Arg::with_name("BOOK")
             .index(1)
             .help("File containing the book configuration, or a Markdown file when called with --single."));

    let matches = app.get_matches();

    pre_check(&matches);
    matches
}

/// Pre-check the matches to see if there isn't illegal options not detected by clap
fn pre_check(matches: &ArgMatches) {
    if matches.is_present("files") && !matches.is_present("create") {
        print_error("A list of additional files is only valid with the --create option.");
    }
}
<|endoftext|>


<commit_before><commit_msg>only ensure ordering is initialized from one place<commit_after><|endoftext|>


<commit_before>

struct CircularBuffer<T : Copy> {
  seqno : usize,
  data  : Vec<T>,
}

impl <T : Copy> CircularBuffer<T> {
  fn new(size : usize, default_value : T) -> CircularBuffer<T> {

    if size == 0 { panic!("size cannot be zero"); }

    let mut ret = CircularBuffer {
      seqno : 0,
      data  : vec![],
    };

    // make sure there is enough place and fill it with the
    // default value
    ret.data.resize(size, default_value);

    // hand over the object
    ret
  }

  fn put<F>(&mut self, setter: F) -> usize
    where F : Fn(&mut T)
  {
    // calculate where to put the data
    let pos = self.seqno % self.data.len();

    // get a reference to the data
    let mut opt : Option<&mut T> = self.data.get_mut(pos);

    // make sure the index worked
    match opt.as_mut() {
      Some(v) => setter(v),
      None    => { panic!("out of bounds {}", pos); }
    }

    // increase sequence number
    self.seqno = self.seqno + 1;
    self.seqno
  }
}

pub fn tests() {
  let mut x = CircularBuffer::new(4, 0 as i32);

  x.put(|v| *v = 1);
}

#[test]
#[should_panic]
fn create_zero_sized() {
  let _x = CircularBuffer::new(0, 0 as i32);
}

#[test]
fn create_non_zero_sized() {
  let _x = CircularBuffer::new(1, 0 as i32);
}

#[test]
fn can_put() {
  let mut x = CircularBuffer::new(1, 0 as i32);
  x.put(|v| *v = 1);
}
<commit_msg>minor changes<commit_after>

struct CircularBuffer<T : Copy> {
  seqno : usize,
  data  : Vec<T>,
}

impl <T : Copy> CircularBuffer<T> {
  fn new(size : usize, default_value : T) -> CircularBuffer<T> {

    if size == 0 { panic!("size cannot be zero"); }

    let mut ret = CircularBuffer {
      seqno : 0,
      data  : vec![],
    };

    // make sure there is enough place and fill it with the
    // default value
    ret.data.resize(size, default_value);
    ret
  }

  fn put<F>(&mut self, setter: F) -> usize
    where F : Fn(&mut T)
  {
    // calculate where to put the data
    let pos = self.seqno % self.data.len();

    // get a reference to the data
    let mut opt : Option<&mut T> = self.data.get_mut(pos);

    // make sure the index worked
    match opt.as_mut() {
      Some(v) => setter(v),
      None    => { panic!("out of bounds {}", pos); }
    }

    // increase sequence number
    self.seqno = self.seqno + 1;
    self.seqno
  }
}

pub fn tests() {
  let mut x = CircularBuffer::new(4, 0 as i32);
  x.put(|v| *v = 1);
}

#[test]
#[should_panic]
fn create_zero_sized() {
  let _x = CircularBuffer::new(0, 0 as i32);
}

#[test]
fn create_non_zero_sized() {
  let _x = CircularBuffer::new(1, 0 as i32);
}

#[test]
fn can_put() {
  let mut x = CircularBuffer::new(1, 0 as i32);
  x.put(|v| *v = 1);
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::error::Error;
use std::result;
use std::fmt;
use snap;
use lz4_compress as lz4;

type Result<T> = result::Result<T, CompressionError>;

pub const LZ4: &'static str = "lz4";
pub const SNAPPY: &'static str = "snappy";

/// It's an error which may occure during encoding or deconding
/// frame body. As there are only two types of compressors it
/// contains two related enum options.
#[derive(Debug)]
pub enum CompressionError {
    /// Snappy error.
    Snappy(Box<Error>),
    /// Lz4 error.
    Lz4(String),
}

impl fmt::Display for CompressionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &CompressionError::Snappy(ref err) => write!(f, "Snappy Error: {:?}", err),
            &CompressionError::Lz4(ref s) => write!(f, "Lz4 Error: {:?}", s),
        }
    }
}

impl Error for CompressionError {
    fn description(&self) -> &str {
        let desc = match self {
            &CompressionError::Snappy(ref err) => err.description(),
            &CompressionError::Lz4(ref s) => s.as_str(),
        };

        return desc;
    }
}

/// Compressor trait that defines functionality
/// which should be provided by typical compressor.
pub trait Compressor {
    /// Encodes given bytes and returns `Result` that contains either
    /// encoded data or an error which occures during the transformation.
    fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Encodes given encoded data and returns `Result` that contains either
    /// encoded bytes or an error which occures during the transformation.
    fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Returns a string which is a name of a compressor. This name should be
    /// exactly the same as one which returns a server in a response to
    /// `Options` request.
    fn into_string(&self) -> Option<String>;
}

/// Enum which represents a type of compression. Only non-startup frame's body can be compressen.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Compression {
    /// [lz4](https://code.google.com/p/lz4/) compression
    Lz4,
    /// [snappy](https://code.google.com/p/snappy/) compression
    Snappy,
    /// Non compression
    None,
}

impl Compression {
    /// It encodes `bytes` basing on type of `Compression`..
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///
    ///   let snappy_compression = Compression::Snappy;
    ///   let bytes = String::from("Hello World").into_bytes().to_vec();
    ///   let encoded = snappy_compression.encode(bytes.clone()).unwrap();
    ///   assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    ///
    /// ```
    pub fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::encode_lz4(bytes),
            &Compression::Snappy => Compression::encode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It decodes `bytes` basing on type of compression.
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///     let lz4_compression = Compression::Lz4;
    ///     let bytes = String::from("Hello World").into_bytes().to_vec();
    ///     let encoded = lz4_compression.encode(bytes.clone()).unwrap();
    ///     let len = encoded.len() as u8;
    ///     let mut input = vec![0, 0, 0, len];
    ///     input.extend_from_slice(encoded.as_slice());
    ///     assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    /// ```
    pub fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::decode_lz4(bytes),
            &Compression::Snappy => Compression::decode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It transforms compression method into a `&str`.
    pub fn as_str(&self) -> Option<&'static str> {
        return match self {
            &Compression::Lz4 => Some(LZ4),
            &Compression::Snappy => Some(SNAPPY),
            &Compression::None => None,
        };
    }

    fn encode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut encoder = snap::Encoder::new();
        return encoder.compress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn decode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut decoder = snap::Decoder::new();
        return decoder.decompress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn encode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        return Ok(lz4::compress(bytes.as_slice()));
    }

    fn decode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        // skip first 4 bytes in accordance to
        // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L805
        return lz4::decompress(&bytes[4..])
            .map_err(|err| CompressionError::Lz4(err.description().to_string()));
    }
}

impl From<String> for Compression {
    /// It converts `String` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_string: String) -> Compression {
        return Compression::from(compression_string.as_str());
    }
}

impl<'a> From<&'a str> for Compression {
    /// It converts `str` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_str: &'a str) -> Compression {
        return match compression_str {
            LZ4 => Compression::Lz4,
            SNAPPY => Compression::Snappy,
            _ => Compression::None,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_compression_from_str() {
        let lz4 = "lz4";
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy";
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x";
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_from_string() {
        let lz4 = "lz4".to_string();
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy".to_string();
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x".to_string();
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_encode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = snappy_compression.encode(bytes.clone()).unwrap();
        assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_lz4() {
        let snappy_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_lz4() {
        let lz4_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let len = encoded.len() as u8;
        let mut input = vec![0, 0, 0, len];
        input.extend_from_slice(encoded.as_slice());
        assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        none_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = none_compression.encode(bytes.clone()).unwrap();
        assert_eq!(none_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_snappy_with_non_utf8() {
        let lz4_compression = Compression::Lz4;
        let bytes: Vec<u8> = vec![0x7f, 0x7f, 0x7f, 0x7f, 0x7f];
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let decode = lz4_compression.decode(encoded);
        assert_eq!(decode.is_err(), true);
    }

}
<commit_msg>Added one more test case<commit_after>use std::convert::From;
use std::error::Error;
use std::result;
use std::fmt;
use snap;
use lz4_compress as lz4;

type Result<T> = result::Result<T, CompressionError>;

pub const LZ4: &'static str = "lz4";
pub const SNAPPY: &'static str = "snappy";

/// It's an error which may occure during encoding or deconding
/// frame body. As there are only two types of compressors it
/// contains two related enum options.
#[derive(Debug)]
pub enum CompressionError {
    /// Snappy error.
    Snappy(Box<Error>),
    /// Lz4 error.
    Lz4(String),
}

impl fmt::Display for CompressionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &CompressionError::Snappy(ref err) => write!(f, "Snappy Error: {:?}", err),
            &CompressionError::Lz4(ref s) => write!(f, "Lz4 Error: {:?}", s),
        }
    }
}

impl Error for CompressionError {
    fn description(&self) -> &str {
        let desc = match self {
            &CompressionError::Snappy(ref err) => err.description(),
            &CompressionError::Lz4(ref s) => s.as_str(),
        };

        return desc;
    }
}

/// Compressor trait that defines functionality
/// which should be provided by typical compressor.
pub trait Compressor {
    /// Encodes given bytes and returns `Result` that contains either
    /// encoded data or an error which occures during the transformation.
    fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Encodes given encoded data and returns `Result` that contains either
    /// encoded bytes or an error which occures during the transformation.
    fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Returns a string which is a name of a compressor. This name should be
    /// exactly the same as one which returns a server in a response to
    /// `Options` request.
    fn into_string(&self) -> Option<String>;
}

/// Enum which represents a type of compression. Only non-startup frame's body can be compressen.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Compression {
    /// [lz4](https://code.google.com/p/lz4/) compression
    Lz4,
    /// [snappy](https://code.google.com/p/snappy/) compression
    Snappy,
    /// Non compression
    None,
}

impl Compression {
    /// It encodes `bytes` basing on type of `Compression`..
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///
    ///   let snappy_compression = Compression::Snappy;
    ///   let bytes = String::from("Hello World").into_bytes().to_vec();
    ///   let encoded = snappy_compression.encode(bytes.clone()).unwrap();
    ///   assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    ///
    /// ```
    pub fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::encode_lz4(bytes),
            &Compression::Snappy => Compression::encode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It decodes `bytes` basing on type of compression.
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///     let lz4_compression = Compression::Lz4;
    ///     let bytes = String::from("Hello World").into_bytes().to_vec();
    ///     let encoded = lz4_compression.encode(bytes.clone()).unwrap();
    ///     let len = encoded.len() as u8;
    ///     let mut input = vec![0, 0, 0, len];
    ///     input.extend_from_slice(encoded.as_slice());
    ///     assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    /// ```
    pub fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::decode_lz4(bytes),
            &Compression::Snappy => Compression::decode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It transforms compression method into a `&str`.
    pub fn as_str(&self) -> Option<&'static str> {
        return match self {
            &Compression::Lz4 => Some(LZ4),
            &Compression::Snappy => Some(SNAPPY),
            &Compression::None => None,
        };
    }

    fn encode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut encoder = snap::Encoder::new();
        return encoder.compress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn decode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut decoder = snap::Decoder::new();
        return decoder.decompress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn encode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        return Ok(lz4::compress(bytes.as_slice()));
    }

    fn decode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        // skip first 4 bytes in accordance to
        // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L805
        return lz4::decompress(&bytes[4..])
            .map_err(|err| CompressionError::Lz4(err.description().to_string()));
    }
}

impl From<String> for Compression {
    /// It converts `String` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_string: String) -> Compression {
        return Compression::from(compression_string.as_str());
    }
}

impl<'a> From<&'a str> for Compression {
    /// It converts `str` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_str: &'a str) -> Compression {
        return match compression_str {
            LZ4 => Compression::Lz4,
            SNAPPY => Compression::Snappy,
            _ => Compression::None,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_compression_from_str() {
        let lz4 = "lz4";
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy";
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x";
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_from_string() {
        let lz4 = "lz4".to_string();
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy".to_string();
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x".to_string();
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_encode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = snappy_compression.encode(bytes.clone()).unwrap();
        assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_lz4() {
        let snappy_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_lz4() {
        let lz4_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let len = encoded.len() as u8;
        let mut input = vec![0, 0, 0, len];
        input.extend_from_slice(encoded.as_slice());
        assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        none_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = none_compression.encode(bytes.clone()).unwrap();
        assert_eq!(none_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_lz4_with_invalid_input() {
        let lz4_compression = Compression::Lz4;
        let bytes: Vec<u8> = vec![0x7f, 0x7f, 0x7f, 0x7f, 0x7f];
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let decode = lz4_compression.decode(encoded);
        assert_eq!(decode.is_err(), true);
    }

    #[test]
    fn test_compression_encode_snappy_with_non_utf8() {
        let snappy_compression = Compression::Snappy;
        let v = vec![0xff, 0xff];
        let encoded = snappy_compression.encode(v.clone()).expect("Should work without exceptions");
        assert_eq!(snappy_compression.decode(encoded).unwrap(), v);
    }

}
<|endoftext|>