<commit_before>extern crate rustc_serialize;

use rustc_serialize::json::Json;
use rustc_serialize::json::Json::{Array, Object};

// TODO: doc comments

pub enum JsonPathElement {
    Key(&'static str), // Use the value associated with a specific key.
    Only,               // Use the only value in an array or object.
}

use json::JsonPathElement::Key;
use json::JsonPathElement::Only;

// TODO: return &str with a lifetime matching path_elements
// TODO: can I just use {:?} where this is used?
fn pretty_print(path_elements: &[JsonPathElement]) -> String {
    // TODO: add (root) to the beginning always.
    if path_elements.is_empty() {
        "(root)".to_string()
    } else {
        path_elements.into_iter().map(|path_element| {
            match path_element {
                // TODO: why do I need the JsonPathElement:: here? I think I don't.
                &JsonPathElement::Key(ref key) => key.to_string(), // TODO: can I remove to_string()?
                &JsonPathElement::Only => "(only)".to_string(),
            }
        }).collect::<Vec<_>>().join(".")
    }
}

// TODO: document
fn get_json_value<'a>(json: &'a Json, path: &[JsonPathElement], index: usize) ->
    Result<&'a Json, String> {
    if index == path.len() {
        return Ok(json);
    }
    match path[index] {
        JsonPathElement::Key(key) => {
            match json {
                &Object(ref obj) => {
                    match obj.get(key) {
                        Some(value) => get_json_value(value, path, index + 1),
                        None => Err(format!("Key {} not found in {}", key, pretty_print(&path[0 .. index]))),
                    }
                }
                _ => Err(format!("Asked for key {} in {}, but value is not an object",
                         key, pretty_print(&path[0 .. index]))),
            }
        },
        JsonPathElement::Only => {
            match json {
                &Object(ref obj) =>
                    if obj.len() == 1 {
                        get_json_value(obj.values().next().unwrap(), path, index + 1)
                    } else {
                        Err(format!("Asked for only key in {}, but object has {} values",
                                    pretty_print(&path[0 .. index]), obj.len()))
                    },
                &Array(ref vec) =>
                    if vec.len() == 1 {
                        get_json_value(vec.first().unwrap(), path, index + 1)
                    } else {
                        Err(format!("Asked for only key in {}, but array has {} elements",
                                    pretty_print(&path[0 .. index]), vec.len()))
                    },
                _ => Err(format!("Asked for only key in {}, but value is not an object or array",
                                 pretty_print(&path[0 .. index]))),
            }
        },
    }
}

pub fn get_json_array<'a>(json: &'a Json, path: &[JsonPathElement]) -> Result<&'a Vec<Json>, String> {
    match get_json_value(json, path, 0) {
        Ok(&Json::Array(ref value)) => Ok(value),
        Ok(..) => Err(format!("Asked for array {}, but value is not an array",
                              pretty_print(&path[..]))),
        Err(message) => Err(message),
    }
}

pub fn get_json_number(json: &Json, path: &[JsonPathElement]) -> Result<u64, String> {
    match get_json_value(json, path, 0) {
        Ok(ref value) => {
            if value.is_number() {
                Ok(value.as_u64().unwrap())
            } else {
                Err(format!("Asked for number {}, but value is not a number",
                            pretty_print(&path[..])))
            }
        },
        Err(message) => Err(message),
    }
}

pub fn get_json_string<'a>(json: &'a Json, path: &[JsonPathElement]) -> Result<&'a str, String> {
    match get_json_value(json, path, 0) {
        Ok(&Json::String(ref value)) => Ok(value),
        Ok(..) => Err(format!("Asked for string {}, but value is not a string",
                              pretty_print(&path[..]))),
        Err(message) => Err(message),
    }
}

#[cfg(test)]
mod tests {
    use super::{get_json_array, get_json_string, get_json_number};
    use super::JsonPathElement::*;
    use rustc_serialize::json::Json;

    #[test]
    fn test_get_json_value_key() {
        assert_eq!(Ok("val"),
                   get_json_string(&Json::from_str("{\"key\": \"val\"}").unwrap(),
                                  &[Key("key")]))
    }

    #[test]
    fn test_get_json_value_only_with_object() {
        let json = Json::from_str("{\"key\": \"val\"}").unwrap();
        let value = get_json_string(&json, &[Only]);
        assert_eq!(Ok("val"), value);
    }

    #[test]
    fn test_get_json_value_only_with_array() {
        let json = Json::from_str("[\"val1\"]").unwrap();
        let value = get_json_string(&json, &[Only]);
        assert_eq!(Ok("val1"), value);
    }

    #[test]
    fn test_get_json_value_multiple_elements() {
        assert_eq!(
            Ok("val3"),
            get_json_string(
                &Json::from_str(
                    "{\"key1\": \"val1\", \"key2\": [{\"key3\": \"val3\"}]}").unwrap(),
                &[Key("key2"), Only, Only]))
    }

    fn assert_error_message<T>(result: &Result<T, String>, expected_message: &str) {
        match result {
            &Ok(..) => panic!(format!("Expected error message: \"{}\"", expected_message)),
            &Err(ref message) if message == expected_message => return,
            &Err(ref message) => panic!(format!("Wrong error message: \"{}\"", message)),
        }
    }

    #[test]
    fn test_get_json_value_key_not_found() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": \"val\"}").unwrap(), &[Key("wrong_key")]),
            "Key wrong_key not found in (root)");
    }

    #[test]
    fn test_get_json_value_key_not_object() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": false}",
                      "{\"key1\": \"val1\"}",
                      "{\"key1\": [1, 2, 3]}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_string(&Json::from_str(json).unwrap(), &[Key("key1"), Key("key2")]),
                "Asked for key key2 in key1, but value is not an object");
        }
    }

    #[test]
    fn test_get_json_value_only_object_empty() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": {}}").unwrap(), &[Key("key"), Only]),
            "Asked for only key in key, but object has 0 values");
    }

    #[test]
    fn test_get_json_value_only_object_multiple_values() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": {\"key1\": \"val1\", \"key2\": \"val2\"}}").unwrap(),
                &[Key("key"), Only]),
            "Asked for only key in key, but object has 2 values");
    }

    #[test]
    fn test_get_json_value_only_array_empty() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": []}").unwrap(), &[Key("key"), Only]),
            "Asked for only key in key, but array has 0 elements");
    }

    #[test]
    fn test_get_json_value_only_array_multiple_values() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": [\"val1\", \"val2\"]}").unwrap(),
                &[Key("key"), Only]),
            "Asked for only key in key, but array has 2 elements");
    }

    #[test]
    fn test_get_json_value_only_not_object_or_array() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": false}",
                      "{\"key1\": \"val1\"}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_string(&Json::from_str(json).unwrap(), &[Key("key1"), Only]),
                "Asked for only key in key1, but value is not an object or array");
        }
    }

    #[test]
    fn test_get_json_array_wrong_type() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": \"val1\"}",
                      "{\"key1\": false}",
                      "{\"key1\": {\"key2\": \"val1\"}}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_array(&Json::from_str(json).unwrap(), &[Key("key1")]),
                "Asked for array key1, but value is not an array");
        }
    }

    #[test]
    fn test_get_json_number_wrong_type() {
        for json in &["{\"key1\": \"val1\"}",
                      "{\"key1\": false}",
                      "{\"key1\": [\"val1\"]}",
                      "{\"key1\": {\"key2\": \"val1\"}}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_number(&Json::from_str(json).unwrap(), &[Key("key1")]),
                "Asked for number key1, but value is not a number");
        }
    }

    #[test]
    fn test_get_json_string_wrong_type() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": false}",
                      "{\"key1\": [\"val1\"]}",
                      "{\"key1\": {\"key2\": \"val1\"}}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_string(&Json::from_str(json).unwrap(), &[Key("key1")]),
                "Asked for string key1, but value is not a string");
        }
    }
}
<commit_msg>Remove a TODO that can't be satisfied.<commit_after>extern crate rustc_serialize;

use rustc_serialize::json::Json;
use rustc_serialize::json::Json::{Array, Object};

// TODO: doc comments

pub enum JsonPathElement {
    Key(&'static str), // Use the value associated with a specific key.
    Only,               // Use the only value in an array or object.
}

use json::JsonPathElement::Key;
use json::JsonPathElement::Only;

// TODO: return &str with a lifetime matching path_elements
// TODO: can I just use {:?} where this is used?
fn pretty_print(path_elements: &[JsonPathElement]) -> String {
    // TODO: add (root) to the beginning always.
    if path_elements.is_empty() {
        "(root)".to_string()
    } else {
        path_elements.into_iter().map(|path_element| {
            match path_element {
                // TODO: why do I need the JsonPathElement:: here? I think I don't.
                &JsonPathElement::Key(ref key) => key.to_string(),
                &JsonPathElement::Only => "(only)".to_string(),
            }
        }).collect::<Vec<_>>().join(".")
    }
}

// TODO: document
fn get_json_value<'a>(json: &'a Json, path: &[JsonPathElement], index: usize) ->
    Result<&'a Json, String> {
    if index == path.len() {
        return Ok(json);
    }
    match path[index] {
        JsonPathElement::Key(key) => {
            match json {
                &Object(ref obj) => {
                    match obj.get(key) {
                        Some(value) => get_json_value(value, path, index + 1),
                        None => Err(format!("Key {} not found in {}", key, pretty_print(&path[0 .. index]))),
                    }
                }
                _ => Err(format!("Asked for key {} in {}, but value is not an object",
                         key, pretty_print(&path[0 .. index]))),
            }
        },
        JsonPathElement::Only => {
            match json {
                &Object(ref obj) =>
                    if obj.len() == 1 {
                        get_json_value(obj.values().next().unwrap(), path, index + 1)
                    } else {
                        Err(format!("Asked for only key in {}, but object has {} values",
                                    pretty_print(&path[0 .. index]), obj.len()))
                    },
                &Array(ref vec) =>
                    if vec.len() == 1 {
                        get_json_value(vec.first().unwrap(), path, index + 1)
                    } else {
                        Err(format!("Asked for only key in {}, but array has {} elements",
                                    pretty_print(&path[0 .. index]), vec.len()))
                    },
                _ => Err(format!("Asked for only key in {}, but value is not an object or array",
                                 pretty_print(&path[0 .. index]))),
            }
        },
    }
}

pub fn get_json_array<'a>(json: &'a Json, path: &[JsonPathElement]) -> Result<&'a Vec<Json>, String> {
    match get_json_value(json, path, 0) {
        Ok(&Json::Array(ref value)) => Ok(value),
        Ok(..) => Err(format!("Asked for array {}, but value is not an array",
                              pretty_print(&path[..]))),
        Err(message) => Err(message),
    }
}

pub fn get_json_number(json: &Json, path: &[JsonPathElement]) -> Result<u64, String> {
    match get_json_value(json, path, 0) {
        Ok(ref value) => {
            if value.is_number() {
                Ok(value.as_u64().unwrap())
            } else {
                Err(format!("Asked for number {}, but value is not a number",
                            pretty_print(&path[..])))
            }
        },
        Err(message) => Err(message),
    }
}

pub fn get_json_string<'a>(json: &'a Json, path: &[JsonPathElement]) -> Result<&'a str, String> {
    match get_json_value(json, path, 0) {
        Ok(&Json::String(ref value)) => Ok(value),
        Ok(..) => Err(format!("Asked for string {}, but value is not a string",
                              pretty_print(&path[..]))),
        Err(message) => Err(message),
    }
}

#[cfg(test)]
mod tests {
    use super::{get_json_array, get_json_string, get_json_number};
    use super::JsonPathElement::*;
    use rustc_serialize::json::Json;

    #[test]
    fn test_get_json_value_key() {
        assert_eq!(Ok("val"),
                   get_json_string(&Json::from_str("{\"key\": \"val\"}").unwrap(),
                                  &[Key("key")]))
    }

    #[test]
    fn test_get_json_value_only_with_object() {
        let json = Json::from_str("{\"key\": \"val\"}").unwrap();
        let value = get_json_string(&json, &[Only]);
        assert_eq!(Ok("val"), value);
    }

    #[test]
    fn test_get_json_value_only_with_array() {
        let json = Json::from_str("[\"val1\"]").unwrap();
        let value = get_json_string(&json, &[Only]);
        assert_eq!(Ok("val1"), value);
    }

    #[test]
    fn test_get_json_value_multiple_elements() {
        assert_eq!(
            Ok("val3"),
            get_json_string(
                &Json::from_str(
                    "{\"key1\": \"val1\", \"key2\": [{\"key3\": \"val3\"}]}").unwrap(),
                &[Key("key2"), Only, Only]))
    }

    fn assert_error_message<T>(result: &Result<T, String>, expected_message: &str) {
        match result {
            &Ok(..) => panic!(format!("Expected error message: \"{}\"", expected_message)),
            &Err(ref message) if message == expected_message => return,
            &Err(ref message) => panic!(format!("Wrong error message: \"{}\"", message)),
        }
    }

    #[test]
    fn test_get_json_value_key_not_found() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": \"val\"}").unwrap(), &[Key("wrong_key")]),
            "Key wrong_key not found in (root)");
    }

    #[test]
    fn test_get_json_value_key_not_object() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": false}",
                      "{\"key1\": \"val1\"}",
                      "{\"key1\": [1, 2, 3]}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_string(&Json::from_str(json).unwrap(), &[Key("key1"), Key("key2")]),
                "Asked for key key2 in key1, but value is not an object");
        }
    }

    #[test]
    fn test_get_json_value_only_object_empty() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": {}}").unwrap(), &[Key("key"), Only]),
            "Asked for only key in key, but object has 0 values");
    }

    #[test]
    fn test_get_json_value_only_object_multiple_values() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": {\"key1\": \"val1\", \"key2\": \"val2\"}}").unwrap(),
                &[Key("key"), Only]),
            "Asked for only key in key, but object has 2 values");
    }

    #[test]
    fn test_get_json_value_only_array_empty() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": []}").unwrap(), &[Key("key"), Only]),
            "Asked for only key in key, but array has 0 elements");
    }

    #[test]
    fn test_get_json_value_only_array_multiple_values() {
        assert_error_message(
            &get_json_string(
                &Json::from_str("{\"key\": [\"val1\", \"val2\"]}").unwrap(),
                &[Key("key"), Only]),
            "Asked for only key in key, but array has 2 elements");
    }

    #[test]
    fn test_get_json_value_only_not_object_or_array() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": false}",
                      "{\"key1\": \"val1\"}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_string(&Json::from_str(json).unwrap(), &[Key("key1"), Only]),
                "Asked for only key in key1, but value is not an object or array");
        }
    }

    #[test]
    fn test_get_json_array_wrong_type() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": \"val1\"}",
                      "{\"key1\": false}",
                      "{\"key1\": {\"key2\": \"val1\"}}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_array(&Json::from_str(json).unwrap(), &[Key("key1")]),
                "Asked for array key1, but value is not an array");
        }
    }

    #[test]
    fn test_get_json_number_wrong_type() {
        for json in &["{\"key1\": \"val1\"}",
                      "{\"key1\": false}",
                      "{\"key1\": [\"val1\"]}",
                      "{\"key1\": {\"key2\": \"val1\"}}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_number(&Json::from_str(json).unwrap(), &[Key("key1")]),
                "Asked for number key1, but value is not a number");
        }
    }

    #[test]
    fn test_get_json_string_wrong_type() {
        for json in &["{\"key1\": 4}",
                      "{\"key1\": false}",
                      "{\"key1\": [\"val1\"]}",
                      "{\"key1\": {\"key2\": \"val1\"}}",
                      "{\"key1\": null}"] {
            assert_error_message(
                &get_json_string(&Json::from_str(json).unwrap(), &[Key("key1")]),
                "Asked for string key1, but value is not a string");
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]
use std::ops::Add;
use std::collections::HashMap;

use traits::Cell;
use traits::Engine;
use traits::ReprConsumer;
use traits::Grid;
use engine::sequential::Sequential;
use grid::square_moore::SquareGrid;
use repr::CellRepr;
use repr::GridRepr;

/// Implementation of Conway's Game of Life.

#[derive(Clone, Debug)]
enum LifeState {
    Dead,
    Alive
}

#[derive(Clone, Debug)]
struct Life {
    state: LifeState
}

impl Life {

    fn alive_count<'a, I>(&self, neighbors: I) -> i32
        where I: Iterator<Item=Option<&'a Self>> {
        neighbors.map(
            |n| {
                match n {
                    Some(ref cell) => match cell.state {
                        LifeState::Alive => 1,
                        LifeState::Dead => 0
                    },
                    None => 0,
                }
            }
        ).fold(0, Add::add)
    }

    #[inline]
    fn dead_state(&self, alive: i32) -> LifeState {
        match alive {
            3 => LifeState::Alive,
            _ => LifeState::Dead
        }
    }

    #[inline]
    fn alive_state(&self, alive: i32) -> LifeState {
        match alive {
            2 | 3 => LifeState::Alive,
            _ => LifeState::Dead
        }
    }
}

impl Default for Life {

    fn default() -> Self {
        Life { state: LifeState::Dead }
    }
}

const ALIVE: &'static str = "a";
const DEAD: &'static str = "d";
const STATE: &'static str = "s";

impl Cell for Life {

    fn step<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let alive = self.alive_count(neighbors);

        let new_state = match self.state {
            LifeState::Alive => self.alive_state(alive),
            LifeState::Dead => self.dead_state(alive)
        };

        let mut new_cell = self.clone();
        new_cell.state = new_state;

        new_cell
    }

    fn repr(&self, meta: &mut HashMap<&str, &str>) {

        let state_str = match self.state {
            LifeState::Alive => ALIVE,
            LifeState::Dead  => DEAD
        };

        meta.insert(STATE, state_str);
    }

    fn from_repr(&mut self, meta: &HashMap<&str, &str>) {

        let new_state = match meta[STATE] {
            ALIVE => LifeState::Alive,
            DEAD => LifeState::Dead,
            _ => panic!("Unknown state: {}.", meta[STATE])
        };

        self.state = new_state;
    }
}

struct SpinnerTestConsumer {
    vertical: bool
}

impl ReprConsumer for SpinnerTestConsumer {

    fn consume(&mut self, repr: &GridRepr) {
        assert_eq!(repr.cells.len(), 9);

        let alive_cells: Vec<&CellRepr> =
            repr.cells.iter().filter(|c| c.state[STATE] == ALIVE).collect();
        assert_eq!(alive_cells.len(), 3);

        self.vertical = !self.vertical;

        // if spinner is in vertical state
        if alive_cells[0].y == 1 {
            assert!(alive_cells.iter().all(|c| c.y == 1));
            assert!(self.vertical);
        }
        // if spinner is in horizontal state
        else if alive_cells[0].x == 1 {
            assert!(alive_cells.iter().all(|c| c.x == 1));
            assert!(!self.vertical);
        }

    }
}

#[test]
fn test_game_of_life() {

    let mut dead_cell = HashMap::new();
    dead_cell.insert(STATE, DEAD);

    let mut alive_cell = HashMap::new();
    alive_cell.insert(STATE, ALIVE);

    // Vertical spinner
    // D | A | D
    // D | A | D
    // D | A | D
    let mut cells: Vec<CellRepr> = Vec::new();
    for x in 0 .. 3 {
        for y in 0 .. 3 {

            let state = match y {
                1 => Some(&alive_cell),
                _ => Some(&dead_cell)
            };

            cells.push(CellRepr::new(x, y, state));
        }
    }

    {
        let alive_cells: Vec<&CellRepr> =
            cells.iter().filter(|c| c.state[STATE] == ALIVE).collect();
        assert_eq!(alive_cells.len(), 3);
    }

    let grid_repr = GridRepr::new(3, 3, Some(cells));

    let mut grid: SquareGrid<Life> = SquareGrid::new(3, 3);
    grid.from_repr(&grid_repr);

    let consumer = SpinnerTestConsumer { vertical: true };
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}<commit_msg>More precise variable name.<commit_after>#![cfg(test)]
use std::ops::Add;
use std::collections::HashMap;

use traits::Cell;
use traits::Engine;
use traits::ReprConsumer;
use traits::Grid;
use engine::sequential::Sequential;
use grid::square_moore::SquareGrid;
use repr::CellRepr;
use repr::GridRepr;

/// Implementation of Conway's Game of Life.

#[derive(Clone, Debug)]
enum LifeState {
    Dead,
    Alive
}

#[derive(Clone, Debug)]
struct Life {
    state: LifeState
}

impl Life {

    fn alive_count<'a, I>(&self, neighbors: I) -> i32
        where I: Iterator<Item=Option<&'a Self>> {
        neighbors.map(
            |n| {
                match n {
                    Some(ref cell) => match cell.state {
                        LifeState::Alive => 1,
                        LifeState::Dead => 0
                    },
                    None => 0,
                }
            }
        ).fold(0, Add::add)
    }

    #[inline]
    fn dead_state(&self, alive: i32) -> LifeState {
        match alive {
            3 => LifeState::Alive,
            _ => LifeState::Dead
        }
    }

    #[inline]
    fn alive_state(&self, alive: i32) -> LifeState {
        match alive {
            2 | 3 => LifeState::Alive,
            _ => LifeState::Dead
        }
    }
}

impl Default for Life {

    fn default() -> Self {
        Life { state: LifeState::Dead }
    }
}

const ALIVE: &'static str = "a";
const DEAD: &'static str = "d";
const STATE: &'static str = "s";

impl Cell for Life {

    fn step<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let alive_count = self.alive_count(neighbors);

        let new_state = match self.state {
            LifeState::Alive => self.alive_state(alive_count),
            LifeState::Dead => self.dead_state(alive_count)
        };

        let mut new_cell = self.clone();
        new_cell.state = new_state;

        new_cell
    }

    fn repr(&self, meta: &mut HashMap<&str, &str>) {

        let state_str = match self.state {
            LifeState::Alive => ALIVE,
            LifeState::Dead  => DEAD
        };

        meta.insert(STATE, state_str);
    }

    fn from_repr(&mut self, meta: &HashMap<&str, &str>) {

        let new_state = match meta[STATE] {
            ALIVE => LifeState::Alive,
            DEAD => LifeState::Dead,
            _ => panic!("Unknown state: {}.", meta[STATE])
        };

        self.state = new_state;
    }
}

struct SpinnerTestConsumer {
    vertical: bool
}

impl ReprConsumer for SpinnerTestConsumer {

    fn consume(&mut self, repr: &GridRepr) {
        assert_eq!(repr.cells.len(), 9);

        let alive_cells: Vec<&CellRepr> =
            repr.cells.iter().filter(|c| c.state[STATE] == ALIVE).collect();
        assert_eq!(alive_cells.len(), 3);

        self.vertical = !self.vertical;

        // if spinner is in vertical state
        if alive_cells[0].y == 1 {
            assert!(alive_cells.iter().all(|c| c.y == 1));
            assert!(self.vertical);
        }
        // if spinner is in horizontal state
        else if alive_cells[0].x == 1 {
            assert!(alive_cells.iter().all(|c| c.x == 1));
            assert!(!self.vertical);
        }

    }
}

#[test]
fn test_game_of_life() {

    let mut dead_cell = HashMap::new();
    dead_cell.insert(STATE, DEAD);

    let mut alive_cell = HashMap::new();
    alive_cell.insert(STATE, ALIVE);

    // Vertical spinner
    // D | A | D
    // D | A | D
    // D | A | D
    let mut cells: Vec<CellRepr> = Vec::new();
    for x in 0 .. 3 {
        for y in 0 .. 3 {

            let state = match y {
                1 => Some(&alive_cell),
                _ => Some(&dead_cell)
            };

            cells.push(CellRepr::new(x, y, state));
        }
    }

    {
        let alive_cells: Vec<&CellRepr> =
            cells.iter().filter(|c| c.state[STATE] == ALIVE).collect();
        assert_eq!(alive_cells.len(), 3);
    }

    let grid_repr = GridRepr::new(3, 3, Some(cells));

    let mut grid: SquareGrid<Life> = SquareGrid::new(3, 3);
    grid.from_repr(&grid_repr);

    let consumer = SpinnerTestConsumer { vertical: true };
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}<|endoftext|>


<commit_before><commit_msg>Always reset index when query changes<commit_after><|endoftext|>


<commit_before>use clap::{crate_version, App, Arg};
use csv;
use io::Write;
use std::io;
use std::process;
use table;

const DESCRIPTION: &str = "A command to print ASCII table from stdin";
const USAGE_TEMPLATE: &str = r#"
Usage:
    {usage}

Flags:
{flags}
"#;

fn main() {
    let matches = App::new("table")
        .version(crate_version!())
        .author("Naoto Kaneko <naoty.k@gmail.com>")
        .about(DESCRIPTION)
        .template(USAGE_TEMPLATE.trim())
        .version_short("v")
        .arg(
            Arg::with_name("header")
                .short("H")
                .long("header")
                .help("Prints table with headers"),
        )
        .get_matches();

    if let Err(error) = start(matches.is_present("header")) {
        eprintln!("{}", error);
        process::exit(1);
    }
}

fn start(has_headers: bool) -> io::Result<()> {
    let mut reader = csv::ReaderBuilder::new()
        .delimiter(b'\t')
        .has_headers(false)
        .from_reader(io::stdin());

    // let mut writer = ascii_writer::new(io::stdout(), has_headers);
    let mut writer = table::markdown_writer::new(io::stdout());

    for result in reader.records() {
        match result {
            Ok(record) => {
                for field in record.iter() {
                    writer.write(field.as_bytes())?;
                }
                writer.write(b"\n")?;
            }
            Err(error) => {
                eprintln!("failed to read record: {}", error);
                process::exit(1);
            }
        }
    }

    writer.flush()
}
<commit_msg>Add -f option to switch writers<commit_after>use clap::{crate_version, App, Arg};
use csv::ReaderBuilder;
use std::io;
use std::process;
use table::{ascii_writer, markdown_writer};

const DESCRIPTION: &str = "A command to print ASCII table from stdin";
const USAGE_TEMPLATE: &str = r#"
Usage:
    {usage}

Flags:
{flags}

Options:
{options}
"#;

fn main() {
    let matches = App::new("table")
        .version(crate_version!())
        .author("Naoto Kaneko <naoty.k@gmail.com>")
        .about(DESCRIPTION)
        .template(USAGE_TEMPLATE.trim())
        .version_short("v")
        .arg(
            Arg::with_name("format")
                .short("f")
                .long("format")
                .takes_value(true)
                .default_value("")
                .value_name("FORMAT")
                .help("Config input/output data format"),
        )
        .arg(
            Arg::with_name("header")
                .short("H")
                .long("header")
                .help("Prints table with headers"),
        )
        .get_matches();

    if let Err(error) = start(
        matches.value_of("format").unwrap(),
        matches.is_present("header"),
    ) {
        eprintln!("{}", error);
        process::exit(1);
    }
}

fn start(format: &str, has_headers: bool) -> io::Result<()> {
    let mut reader = ReaderBuilder::new()
        .delimiter(b'\t')
        .has_headers(false)
        .from_reader(io::stdin());

    let tokens = format.split(":").take(2);
    let mut ascii_writer = ascii_writer::new(io::stdout(), has_headers);
    let mut markdown_writer = markdown_writer::new(io::stdout());
    let writer: &mut dyn io::Write = match tokens.last() {
        Some("markdown") => &mut markdown_writer,
        _ => &mut ascii_writer,
    };

    for result in reader.records() {
        match result {
            Ok(record) => {
                for field in record.iter() {
                    writer.write(field.as_bytes())?;
                }
                writer.write(b"\n")?;
            }
            Err(error) => {
                eprintln!("failed to read record: {}", error);
                process::exit(1);
            }
        }
    }

    writer.flush()
}
<|endoftext|>


<commit_before>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate lazy_static;

mod lib;

use lib::config;
use lib::db;
use lib::logger;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::DownloadError;
use lib::converter::Converter;

use std::fs::{remove_file,remove_dir_all};

const VERSION : &'static str = "0.4"; // String not valid
const CONFIG_PATH : &'static str = "config.cfg";
const LOG_CONFIG: &'static str = "log.conf";
const LOG_PATTERN: &'static str = "%d{%d-%m-%Y %H:%M:%S}\t[%l]\t%f:%L \t%m";
const SLEEP_MS: u32 = 5000;
const CODE_IN_PROGRESS: i8 = 1;
const CODE_SUCCESS: i8 = 2;
const CODE_SUCCESS_WARNINGS: i8 = 3; // finished with warnings
const CODE_FAILED_INTERNAL: i8 = 10; // internal error
const CODE_FAILED_QUALITY: i8 = 11; // qualitz not available
const CODE_FAILED_UNAVAILABLE: i8 = 12; // source unavailable (private / removed)
const TYPE_YT_VIDEO: i16 = 0;
const TYPE_YT_PL: i16 = 1;
const TYPE_TWITCH: i16 = 2;

lazy_static! {
    pub static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

enum Thing { String(String), Bool(bool), None }

fn main() {
    logger::initialize();

    let pool = db::db_connect(db::mysql_options(), SLEEP_MS);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin_dir,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(mut downl_db) = db::request_entry(& pool) {
            print_pause = true;
            let qid = downl_db.qid.clone();
            db::set_query_code(&pool, &CODE_IN_PROGRESS, &downl_db.qid).ok().expect("Failed to set query code!");
            db::set_query_state(&pool.clone(),&qid, "started", false);
            let action_result: Result<Thing,DownloadError>;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if downl_db.playlist {
                    action_result = handle_playlist(& mut downl_db, &converter,&mut left_files);
                }else{
                    action_result = handle_download(& downl_db, &None, &converter,&mut left_files);
                }

                if !left_files.is_empty() {
                    trace!("cleaning up files");
                    for i in &left_files {
                        match remove_file(&i) {
                            Ok(_) => (trace!("cleaning up {}",i)),
                            Err(e) => warn!("unable to remove file '{}' {}",i,e),
                        }
                    }
                }
            }

            let code: i8 = match action_result {
                Ok(t) => {
                    match t {
                        Thing::Bool(b) => {
                            if b { CODE_SUCCESS_WARNINGS } else { CODE_SUCCESS }
                        }
                        _ => CODE_SUCCESS,
                    }
                },
                Err(e) => {
                    warn!("Error: {:?}", e);
                    if downl_db.source_type == TYPE_TWITCH {
                        match lib::cleanup_temp_folder() {
                            Err(e) => error!("error doing cleanup {:?}", e),
                            Ok(_) => (),
                        }
                    }
                    match e {
                        DownloadError::NotAvailable => CODE_FAILED_UNAVAILABLE,
                        DownloadError::ExtractorError => CODE_FAILED_UNAVAILABLE,
                        DownloadError::QualityNotAvailable => CODE_FAILED_QUALITY,
                        _ => {
                            let details = match e {
                                DownloadError::DBError(s) => s,
                                DownloadError::DownloadError(s) => s,
                                DownloadError::FFMPEGError(s) => s,
                                DownloadError::InternalError(s) => s,
                                _ => unreachable!(),
                            };
                            db::add_query_status(&pool,&qid, &details);
                            CODE_FAILED_INTERNAL
                        },
                    }
                }
            };
            db::set_query_code(&pool, &code,&qid).ok().expect("Failed to set query code!");
			db::set_null_state(&pool, &qid);

        } else {
            if print_pause { debug!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }
}

/// Download handler
/// Used by the playlist/file handler to download one file
/// Based on the quality it needs to download audio & video separately and convert them together
/// In case of a playlist download it depends on the target download folder & if it should be bezipped
/// In case of a DMCA we need to download the file via the socket connector,
/// which will output a mp3, or if requested, the video but with the highest quality.
/// Thus in case of a DMCA we can't pick a quality anymore.
/// Also the filename depends on the socket output then.
///
/// If it's a non-zipped single file, it's moved after a successful download, converted etc to the
/// main folder from which it should be downloadable.
/// The original non-ascii & url_encode'd name of the file is stored in the DB
fn handle_download<'a>(downl_db: &DownloadDB, folder: &Option<String>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, 0,false);
    }

    let download = Downloader::new(&downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded

    let temp_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            info!("DMCA error!");
            if CONFIG.general.lib_use {
                match download.lib_request_video(1,0, &temp_path) {
                    Err(err) => { warn!("lib-call error {:?}", err); return Err(err); },
                    Ok(v) => { dmca = true; v },
                }
            } else {
                return Err(DownloadError::NotAvailable);
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            error!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    file_db.push(temp_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, lib::get_file_ext(&download));

    trace!("Filename: {}", name);

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality, &downl_db.source_type)
    };
    let convert_audio = CONFIG.extensions.mp3.contains(&downl_db.quality);

    let total_steps = if dmca {
        if download.is_audio() {
            3
        } else {
            2
        }
    } else if is_splitted_video {
        4
    } else if download.is_audio() {
        3
    } else {
        2
    };

    if !dmca { // on dmca the called lib handles splitt videos, only audio conversion has to handled by us
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        match download.download_file(&temp_path, convert_audio) {
            Err(e) => {file_db.pop(); return Err(e); },
            Ok(_) => (),
        }

        if is_splitted_video {
            debug!("splitted video");
            // download audio file & convert together
            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 3, total_steps,false);
            }

            let audio_path = lib::format_file_path(&downl_db.qid, folder.clone(), true);

            trace!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));
            file_db.push(audio_path.clone());

            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 4, total_steps,false);
            }

            match converter.merge_files(&downl_db.qid,&temp_path, &audio_path,&save_path.to_string_lossy(), !downl_db.compress) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));
            file_db.pop();
        }
    }

    if !is_splitted_video { // if it's no splitted video & no audio we're moving the file, regardless of an earlier DMCA (lib call)
        debug!("no split container");
        if !download.is_audio() {
            try!(lib::move_file(&temp_path, &save_path));
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3 or extract m4a, directly to output file
    	debug!("is audio file");
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&temp_path, &save_path.to_string_lossy(), convert_audio));
        try!(remove_file(&temp_path));
    }else{
        debug!("no audio");
        if is_splitted_video {
            try!(remove_file(&temp_path));
        }
    }
    file_db.pop();

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        try!(db::add_file_entry(&downl_db.pool.clone(), &downl_db.qid,&save_path.file_name().unwrap().to_string_lossy(), &name));
    }
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, true);
    }

    if folder.is_some() {
        Ok(Thing::String(save_path.to_string_lossy().into_owned()))
    } else {
        Ok(Thing::None)
    }
}

/// Handles a playlist request
/// If zipping isn't requested the downloads will be split up,
/// so for each video in the playlist an own query entry will be created
/// if warnings occured (unavailable video etc) the return will be true
fn handle_playlist<'a>(downl_db: &'a mut DownloadDB<'a>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, max_steps,false);

    let pl_id: i64 = downl_db.qid;
    downl_db.update_folder(format!("{}/{}",&CONFIG.general.temp_dir,pl_id));
    trace!("Folder:  {}",downl_db.folder);

    let db_copy = downl_db.clone();
    let download = Downloader::new(&db_copy, &CONFIG.general);

    let mut playlist_name = String::new();
    if downl_db.compress {
        playlist_name = try!(download.get_playlist_name());
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    trace!("retriving playlist videos..");
    let file_ids: Vec<String> = try!(download.get_playlist_ids());

    let handler_folder = if downl_db.compress {
        Some(pl_id.to_string())
    }else {
        None
    };

    trace!("got em");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut warnings = false;
    let mut current_url: String;
    let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");
    // we'll store all files and delete em later, so we don't need rm -rf
    let mut file_delete_list: Vec<String> = Vec::with_capacity(if downl_db.compress { file_ids.len() } else { 0 });
    for id in file_ids.iter() {
        current_step += 1;
        if downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        current_url = format!("https://www.youtube.com/watch?v={}",id);
        downl_db.update_video(current_url.clone(), current_step as i64);
        debug!("id: {}",id);
        match handle_download(downl_db, &handler_folder, converter, file_db) {
            Err(e) => { warn!("error downloading {}: {:?}",id,e);
                        failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                        warnings = true;
            },
            Ok(e) => {  if downl_db.compress {
                            match e {
                                Thing::String(v) => file_delete_list.push(v),
                                _ => {error!("handle_download not returning a filename"); panic!();},
                            }
                        }
            },
        }
    }

    if warnings {
        db::add_query_status(&downl_db.pool.clone(),&pl_id, &failed_log);
    }

    trace!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        let zip_name = format!("{}.zip",playlist_name);
        let zip_file = lib::format_save_path(None, &lib::url_sanitize(&playlist_name),"zip");
        trace!("zip file: {} \n zip source {}",zip_file.to_string_lossy(), &downl_db.folder);
        try!(lib::zip_folder(&downl_db.folder, zip_file));
        try!(db::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_name, &playlist_name));

        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir_all(&downl_db.folder));
        file_db.pop();
    }

    Ok(Thing::Bool(warnings))
}
<commit_msg>ref. change now stable thanks to pingveno @ irc<commit_after>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate lazy_static;

mod lib;

use lib::config;
use lib::db;
use lib::logger;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::DownloadError;
use lib::converter::Converter;

use std::fs::{remove_file,remove_dir_all};

const VERSION : &'static str = "0.4"; // String not valid
const CONFIG_PATH : &'static str = "config.cfg";
const LOG_CONFIG: &'static str = "log.conf";
const LOG_PATTERN: &'static str = "%d{%d-%m-%Y %H:%M:%S}\t[%l]\t%f:%L \t%m";
const SLEEP_MS: u32 = 5000;
const CODE_IN_PROGRESS: i8 = 1;
const CODE_SUCCESS: i8 = 2;
const CODE_SUCCESS_WARNINGS: i8 = 3; // finished with warnings
const CODE_FAILED_INTERNAL: i8 = 10; // internal error
const CODE_FAILED_QUALITY: i8 = 11; // qualitz not available
const CODE_FAILED_UNAVAILABLE: i8 = 12; // source unavailable (private / removed)
const TYPE_YT_VIDEO: i16 = 0;
const TYPE_YT_PL: i16 = 1;
const TYPE_TWITCH: i16 = 2;

lazy_static! {
    pub static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

enum Thing { String(String), Bool(bool), None }

fn main() {
    logger::initialize();

    let pool = db::db_connect(db::mysql_options(), SLEEP_MS);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin_dir,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(mut downl_db) = db::request_entry(& pool) {
            print_pause = true;
            let qid = downl_db.qid.clone();
            db::set_query_code(&pool, &CODE_IN_PROGRESS, &downl_db.qid).ok().expect("Failed to set query code!");
            db::set_query_state(&pool.clone(),&qid, "started", false);
            let action_result: Result<Thing,DownloadError>;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if downl_db.playlist {
                    action_result = handle_playlist(& mut downl_db, &converter,&mut left_files);
                }else{
                    action_result = handle_download(& downl_db, &None, &converter,&mut left_files);
                }

                if !left_files.is_empty() {
                    trace!("cleaning up files");
                    for i in &left_files {
                        match remove_file(&i) {
                            Ok(_) => (trace!("cleaning up {}",i)),
                            Err(e) => warn!("unable to remove file '{}' {}",i,e),
                        }
                    }
                }
            }

            let code: i8 = match action_result {
                Ok(t) => {
                    match t {
                        Thing::Bool(b) => {
                            if b { CODE_SUCCESS_WARNINGS } else { CODE_SUCCESS }
                        }
                        _ => CODE_SUCCESS,
                    }
                },
                Err(e) => {
                    warn!("Error: {:?}", e);
                    if downl_db.source_type == TYPE_TWITCH {
                        match lib::cleanup_temp_folder() {
                            Err(e) => error!("error doing cleanup {:?}", e),
                            Ok(_) => (),
                        }
                    }
                    match e {
                        DownloadError::NotAvailable => CODE_FAILED_UNAVAILABLE,
                        DownloadError::ExtractorError => CODE_FAILED_UNAVAILABLE,
                        DownloadError::QualityNotAvailable => CODE_FAILED_QUALITY,
                        _ => {
                            let details = match e {
                                DownloadError::DBError(s) => s,
                                DownloadError::DownloadError(s) => s,
                                DownloadError::FFMPEGError(s) => s,
                                DownloadError::InternalError(s) => s,
                                _ => unreachable!(),
                            };
                            db::add_query_status(&pool,&qid, &details);
                            CODE_FAILED_INTERNAL
                        },
                    }
                }
            };
            db::set_query_code(&pool, &code,&qid).ok().expect("Failed to set query code!");
			db::set_null_state(&pool, &qid);

        } else {
            if print_pause { debug!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }
}

/// Download handler
/// Used by the playlist/file handler to download one file
/// Based on the quality it needs to download audio & video separately and convert them together
/// In case of a playlist download it depends on the target download folder & if it should be bezipped
/// In case of a DMCA we need to download the file via the socket connector,
/// which will output a mp3, or if requested, the video but with the highest quality.
/// Thus in case of a DMCA we can't pick a quality anymore.
/// Also the filename depends on the socket output then.
///
/// If it's a non-zipped single file, it's moved after a successful download, converted etc to the
/// main folder from which it should be downloadable.
/// The original non-ascii & url_encode'd name of the file is stored in the DB
fn handle_download<'a>(downl_db: &DownloadDB, folder: &Option<String>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, 0,false);
    }

    let download = Downloader::new(&downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded

    let temp_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            info!("DMCA error!");
            if CONFIG.general.lib_use {
                match download.lib_request_video(1,0, &temp_path) {
                    Err(err) => { warn!("lib-call error {:?}", err); return Err(err); },
                    Ok(v) => { dmca = true; v },
                }
            } else {
                return Err(DownloadError::NotAvailable);
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            error!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    file_db.push(temp_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, lib::get_file_ext(&download));

    trace!("Filename: {}", name);

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality, &downl_db.source_type)
    };
    let convert_audio = CONFIG.extensions.mp3.contains(&downl_db.quality);

    let total_steps = if dmca {
        if download.is_audio() {
            3
        } else {
            2
        }
    } else if is_splitted_video {
        4
    } else if download.is_audio() {
        3
    } else {
        2
    };

    if !dmca { // on dmca the called lib handles splitt videos, only audio conversion has to handled by us
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        match download.download_file(&temp_path, convert_audio) {
            Err(e) => {file_db.pop(); return Err(e); },
            Ok(_) => (),
        }

        if is_splitted_video {
            debug!("splitted video");
            // download audio file & convert together
            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 3, total_steps,false);
            }

            let audio_path = lib::format_file_path(&downl_db.qid, folder.clone(), true);

            trace!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));
            file_db.push(audio_path.clone());

            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 4, total_steps,false);
            }

            match converter.merge_files(&downl_db.qid,&temp_path, &audio_path,&save_path.to_string_lossy(), !downl_db.compress) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));
            file_db.pop();
        }
    }

    if !is_splitted_video { // if it's no splitted video & no audio we're moving the file, regardless of an earlier DMCA (lib call)
        debug!("no split container");
        if !download.is_audio() {
            try!(lib::move_file(&temp_path, &save_path));
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3 or extract m4a, directly to output file
    	debug!("is audio file");
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&temp_path, &save_path.to_string_lossy(), convert_audio));
        try!(remove_file(&temp_path));
    }else{
        debug!("no audio");
        if is_splitted_video {
            try!(remove_file(&temp_path));
        }
    }
    file_db.pop();

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        try!(db::add_file_entry(&downl_db.pool.clone(), &downl_db.qid,&save_path.file_name().unwrap().to_string_lossy(), &name));
    }
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, true);
    }

    if folder.is_some() {
        Ok(Thing::String(save_path.to_string_lossy().into_owned()))
    } else {
        Ok(Thing::None)
    }
}

/// Handles a playlist request
/// If zipping isn't requested the downloads will be split up,
/// so for each video in the playlist an own query entry will be created
/// if warnings occured (unavailable video etc) the return will be true
fn handle_playlist<'a>(downl_db: & mut DownloadDB<'a>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, max_steps,false);

    let pl_id: i64 = downl_db.qid;
    downl_db.update_folder(format!("{}/{}",&CONFIG.general.temp_dir,pl_id));
    trace!("Folder:  {}",downl_db.folder);

    let db_copy = downl_db.clone();
    let download = Downloader::new(&db_copy, &CONFIG.general);

    let mut playlist_name = String::new();
    if downl_db.compress {
        playlist_name = try!(download.get_playlist_name());
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    trace!("retriving playlist videos..");
    let file_ids: Vec<String> = try!(download.get_playlist_ids());

    let handler_folder = if downl_db.compress {
        Some(pl_id.to_string())
    }else {
        None
    };

    trace!("got em");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut warnings = false;
    let mut current_url: String;
    let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");
    // we'll store all files and delete em later, so we don't need rm -rf
    let mut file_delete_list: Vec<String> = Vec::with_capacity(if downl_db.compress { file_ids.len() } else { 0 });
    for id in file_ids.iter() {
        current_step += 1;
        if downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        current_url = format!("https://www.youtube.com/watch?v={}",id);
        downl_db.update_video(current_url.clone(), current_step as i64);
        debug!("id: {}",id);
        match handle_download(downl_db, &handler_folder, converter, file_db) {
            Err(e) => { warn!("error downloading {}: {:?}",id,e);
                        failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                        warnings = true;
            },
            Ok(e) => {  if downl_db.compress {
                            match e {
                                Thing::String(v) => file_delete_list.push(v),
                                _ => {error!("handle_download not returning a filename"); panic!();},
                            }
                        }
            },
        }
    }

    if warnings {
        db::add_query_status(&downl_db.pool.clone(),&pl_id, &failed_log);
    }

    trace!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        let zip_name = format!("{}.zip",playlist_name);
        let zip_file = lib::format_save_path(None, &lib::url_sanitize(&playlist_name),"zip");
        trace!("zip file: {} \n zip source {}",zip_file.to_string_lossy(), &downl_db.folder);
        try!(lib::zip_folder(&downl_db.folder, zip_file));
        try!(db::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_name, &playlist_name));

        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir_all(&downl_db.folder));
        file_db.pop();
    }

    Ok(Thing::Bool(warnings))
}
<|endoftext|>


<commit_before>extern crate irc;

use irc::client::prelude::*;
use std::default::Default;

fn main() {
	let server = IrcServer::from_config(Config{
		owners   : Some(vec!["nabijaczleweli".to_string()]),
		nickname : Some("NabBot".to_string()),
		username : Some("NabBot".to_string()),
		realname : Some("наб's IRC bot".to_string()),
		server   : Some("chat.freenode.net".to_string()),
		use_ssl  : Some(true),
		channels : Some(vec!["#loungecpp".to_string()]),
		user_info: Some("наб's IRC bot".to_string()),
		..Default::default()
	}).unwrap();
	server.identify().unwrap();

	for message in server.iter() {
		if let Ok(message) = message {
			let sender = message.get_source_nickname().map(String::from);

			if let Ok(Command::PRIVMSG(target, msg)) = Command::from_message_io(Ok(message)) {
				if (target == "NabBot" && msg == "Navaer") || msg == "Navaer, NabBot" {
					server.send_privmsg(&*sender.as_ref().unwrap_or(&target), "Mára mesta").unwrap();
					server.send_quit("Mára mesta").unwrap();
				}
			}
		}
	}
}
<commit_msg>Print message when joined<commit_after>extern crate irc;

use irc::client::prelude::*;
use std::default::Default;

fn main() {
	let server = IrcServer::from_config(Config{
		owners   : Some(vec!["nabijaczleweli".to_string()]),
		nickname : Some("NabBot".to_string()),
		username : Some("NabBot".to_string()),
		realname : Some("наб's IRC bot".to_string()),
		server   : Some("chat.freenode.net".to_string()),
		use_ssl  : Some(true),
		channels : Some(vec!["#loungecpp".to_string()]),
		user_info: Some("наб's IRC bot".to_string()),
		..Default::default()
	}).unwrap();
	server.identify().unwrap();

	for message in server.iter() {
		if let Ok(message) = message {
			let sender = message.get_source_nickname().map(String::from);

			match Command::from_message_io(Ok(message)) {
				Ok(Command::JOIN(_, _, _))        => println!("I'm in"),
				Ok(Command::PRIVMSG(target, msg)) => {
					let to_self = target == "NabBot";

					if (to_self && msg == "Navaer") || msg == "Navaer, NabBot" {
						server.send_quit("Mára mesta").unwrap();
					}
				}
				_ => (),
			}
		}
	}
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(plugin, link)] extern crate log;

const U4_MASK: u8 = 0b00001111; // Takes the lower 4 bits of a byte. Useful for decoding & encoding nibbles.

enum CpuState {
	Continue,
	Halt,
}

struct P150Cpu {
	ip:  u8,
	ir: u16,

	reg: [u8, ..16],
	mem: [u8, ..256],
}

impl P150Cpu {
	fn new() -> P150Cpu {
		P150Cpu {
			ip:  0x0,
			ir:  0x00,

			reg: [0u8, ..16],
			mem: [0u8, ..256],
		}
	}

	fn dump(&self) {
		println!("IP: {}, IR: {}", self.ip, self.ir);

		println!("Registers")
		for (addr, cell) in self.reg.iter().enumerate() {
			println!("{}: {}", addr, cell)
		}
	}

	// Read an array of instructions into memory
	fn init_mem(&mut self, memory: &[u16]) {
		let mut next_cell = 0x00;

		for op in memory.iter() {
			let byte_1 = (*op >> 8) as u8;
			let byte_2 = *op as u8;

			self.mem[next_cell]     = byte_1;
			self.mem[next_cell + 1] = byte_2;
			next_cell += 2
		}
	}

	fn tick(&mut self) -> CpuState {
		self.fetch();

		// decode
		let op = (self.ir >> 12) as u8;
		match op {
			0x9   => { // RSET
				let rloc = ((self.ir >> 8) as u8) & U4_MASK; // lower 4 bytes of first byte
				let rval = self.ir as u8;                    // value is entire second byte
				self.reg[rloc as uint] = rval;               // store value in register

				Continue
			},

			0xB   => { // HALT
				Halt
			},
			_     => { debug!("read opcode: {}", op); Continue },
		}
	}

	fn fetch(&mut self) {
		// load PC -> IR
		let byte_1 = self.mem[(self.ip+0) as uint];
		let byte_2 = self.mem[(self.ip+1) as uint];

		self.ir  = (byte_1 as u16 << 8) | (byte_2 as u16);
		self.ip += 2;

		debug!("IR set to {} ({},{})", self.ir, byte_1, byte_2)
	}
}

fn main() {
	let program = [0x911E, 0x920C, 0xB000];
	let mut cpu = P150Cpu::new();
	cpu.init_mem(program);

	loop {
		match cpu.tick() {
			Continue => { continue; },
			Halt => { println!("CPU halted."); cpu.dump(); break; },
		}
	}
}

<commit_msg>Add the `ADDB` insruction (0x0)<commit_after>#![feature(phase)]
#[phase(plugin, link)] extern crate log;

const U4_LOW:  u8 = 0b00001111;
const U4_HIGH: u8 = 0b11110000;

enum CpuState {
	Continue,
	Halt,
}

struct P150Cpu {
	ip:  u8,
	ir: u16,

	reg: [u8, ..16],
	mem: [u8, ..256],
}

impl P150Cpu {
	fn new() -> P150Cpu {
		P150Cpu {
			ip:  0x0,
			ir:  0x00,

			reg: [0u8, ..16],
			mem: [0u8, ..256],
		}
	}

	fn dump(&self) {
		println!("IP: {}, IR: {}", self.ip, self.ir);

		println!("Registers")
		for (addr, cell) in self.reg.iter().enumerate() {
			println!("{}: {}", addr, cell)
		}
	}

	// Read an array of instructions into memory
	fn init_mem(&mut self, memory: &[u16]) {
		let mut next_cell = 0x00;

		for op in memory.iter() {
			let byte_1 = (*op >> 8) as u8;
			let byte_2 = *op as u8;

			self.mem[next_cell]     = byte_1;
			self.mem[next_cell + 1] = byte_2;
			next_cell += 2
		}
	}

	fn tick(&mut self) -> CpuState {
		self.fetch();

		// decode
		let op = (self.ir >> 12) as u8;
		match op {
			0x0   => { // ADDB
				let rloc_i0 = ((self.ir >> 8) as u8) & U4_LOW;   // first input: lower nibble of first byte
				let rloc_i1 = ((self.ir as u8) & U4_HIGH) >> 4;  // second input: upper nibble of second byte
				let rloc_o0 = (self.ir as u8) & U4_LOW;          // output: lower nibble of second byte

				self.reg[rloc_o0 as uint] = self.reg[rloc_i0 as uint] + self.reg[rloc_i1 as uint];
				Continue
			},

			0x9   => { // RSET
				let rloc = ((self.ir >> 8) as u8) & U4_MASK; // lower 4 bytes of first byte
				let rval = self.ir as u8;                    // value is entire second byte
				self.reg[rloc as uint] = rval;               // store value in register

				Continue
			},

			0xB   => { // HALT
				Halt
			},
			_     => { debug!("read opcode: {}", op); Continue },
		}
	}

	fn fetch(&mut self) {
		// load PC -> IR
		let byte_1 = self.mem[(self.ip+0) as uint];
		let byte_2 = self.mem[(self.ip+1) as uint];

		self.ir  = (byte_1 as u16 << 8) | (byte_2 as u16);
		self.ip += 2;

		debug!("IR set to {} ({},{})", self.ir, byte_1, byte_2)
	}
}

fn main() {
	let mut cpu = P150Cpu::new();
	let program = [0x911E, 0x920C, 0x0123, 0xB000];
	cpu.init_mem(program);

	// 0x911E => 0x9 (rset), 0x1 (reg), 0x1E (val: 30)
	// 0x920C => 0x9 (rset), 0x2 (reg), 0x0C (val: 12)
	// 0x0123 => 0x0 (addb), 0x1 (in1), 0x2 (in2), 0x3 (out1)

	loop {
		match cpu.tick() {
			Continue => { continue; },
			Halt => { println!("CPU halted."); cpu.dump(); break; },
		}
	}
}

<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate stats;
extern crate clap;

mod args;
mod http_status;
mod log_parser;
use log_parser::parse_line;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::*;
mod filter;
mod analyzer;
mod render;
use render::*;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn main() {
    let args = args::parse_args(env::args()).unwrap();

    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).unwrap()),
    };

    let (times, first_request) = extract_times(input, &args.conditions);

    let result = analyzer::analyze(&times);

    let mut stream;
    let mut renderer: Box<Renderer>;

    if args.graphite_server.is_some() {
        stream = TcpStream::connect((args.graphite_server.unwrap().as_ref(),
                                             args.graphite_port.unwrap()))
                .expect("Could not connect to the Graphite server");

        renderer = Box::new(GraphiteRenderer::new(
                    UTC::now().with_timezone(&first_request.unwrap().time.timezone()),
                    args.graphite_prefix,
                    &mut stream,
                ));
    } else {
        renderer = Box::new(TerminalRenderer::new());
    }

    match result {
        Some(result) => {
            renderer.render(result);
        }
        None => println_stderr!("No matching log lines in file."),
    }
}

fn extract_times(input: Box<io::Read>,
                 conditions: &filter::FilterConditions)
                 -> (Vec<i64>, Option<Request>) {
    let reader = io::BufReader::new(input);
    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    for line in lines {
        let line_value = &line.unwrap();

        let parsed_line = parse_line(line_value);

        match parsed_line {
            Ok(event) => {
                match event {
                    LogEvent::Request(request) => {
                        if first_request.is_none() {
                            first_request = Some(request.clone());
                        }
                        requests.push(request)
                    }
                    LogEvent::Response(response) => responses.push(response),
                }

                let pairs = extract_matching_request_response_pairs(&mut requests, &mut responses);

                let mut new_times: Vec<i64> = pairs.iter()
                    .filter(|pair| filter::matches_filter(&pair, conditions))
                    .map(|pair| pair.response.response_time.num_milliseconds())
                    .collect();

                times.append(&mut new_times);
            }
            Err(err) => println_stderr!("{}", err),
        }
    }
    (times, first_request)
}

#[test]
fn test_extract_times() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/simple-1.log")
                                                   .unwrap()),
                                               &conditions);

    assert_eq!(times, vec![7, 10]);
    assert_eq!(first_request.unwrap().id, 1);
}

#[test]
fn test_extract_times_ignore_broken_lines() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/broken.log")
                                                   .unwrap()),
                                               &conditions);

    assert_eq!(times, vec![7]);
    assert_eq!(first_request.unwrap().id, 1);
}
<commit_msg>Match instead of if/else<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate stats;
extern crate clap;

mod args;
mod http_status;
mod log_parser;
use log_parser::parse_line;
use log_parser::log_events::*;
mod request_response_matcher;
use request_response_matcher::*;
mod filter;
mod analyzer;
mod render;
use render::*;

// http://stackoverflow.com/a/27590832/376138
macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn main() {
    let args = args::parse_args(env::args()).unwrap();

    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).unwrap()),
    };

    let (times, first_request) = extract_times(input, &args.conditions);

    let result = analyzer::analyze(&times);

    let mut stream;
    let mut renderer: Box<Renderer>;

    renderer = match args.graphite_server {
        Some(graphite_server) => {
            stream = TcpStream::connect((graphite_server.as_ref(),
                                         args.graphite_port.unwrap()))
                 .expect("Could not connect to the Graphite server");

            Box::new(GraphiteRenderer::new(
                UTC::now().with_timezone(&first_request.unwrap().time.timezone()),
                args.graphite_prefix,
                &mut stream,
            ))
        },
        None => {
            Box::new(TerminalRenderer::new())
        }
    };

    match result {
        Some(result) => {
            renderer.render(result);
        }
        None => println_stderr!("No matching log lines in file."),
    }
}

fn extract_times(input: Box<io::Read>,
                 conditions: &filter::FilterConditions)
                 -> (Vec<i64>, Option<Request>) {
    let reader = io::BufReader::new(input);
    let lines = reader.lines();

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();
    let mut times: Vec<i64> = Vec::new();

    // We need to store 1 Request in order to determine the timezone later
    let mut first_request: Option<Request> = None;

    for line in lines {
        let line_value = &line.unwrap();

        let parsed_line = parse_line(line_value);

        match parsed_line {
            Ok(event) => {
                match event {
                    LogEvent::Request(request) => {
                        if first_request.is_none() {
                            first_request = Some(request.clone());
                        }
                        requests.push(request)
                    }
                    LogEvent::Response(response) => responses.push(response),
                }

                let pairs = extract_matching_request_response_pairs(&mut requests, &mut responses);

                let mut new_times: Vec<i64> = pairs.iter()
                    .filter(|pair| filter::matches_filter(&pair, conditions))
                    .map(|pair| pair.response.response_time.num_milliseconds())
                    .collect();

                times.append(&mut new_times);
            }
            Err(err) => println_stderr!("{}", err),
        }
    }
    (times, first_request)
}

#[test]
fn test_extract_times() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/simple-1.log")
                                                   .unwrap()),
                                               &conditions);

    assert_eq!(times, vec![7, 10]);
    assert_eq!(first_request.unwrap().id, 1);
}

#[test]
fn test_extract_times_ignore_broken_lines() {
    let conditions = filter::FilterConditions {
        include_terms: None,
        exclude_terms: None,
        latest_time: None,
    };

    let (times, first_request) = extract_times(Box::new(File::open("src/test/broken.log")
                                                   .unwrap()),
                                               &conditions);

    assert_eq!(times, vec![7]);
    assert_eq!(first_request.unwrap().id, 1);
}
<|endoftext|>


<commit_before><commit_msg>rate: Reset log_scale after first frame of each type<commit_after><|endoftext|>