<commit_before>use rusqlite::Connection;
use std::env;
use std::fs::create_dir_all;

#[derive(Debug)]
// A struct to represent a movie to be attached to actors or directors
pub struct Movie {
    pub title: String,
    pub year: String,
}

// Checks if a database already exists
pub fn db_exists() -> bool {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");
    path_buf.as_path().exists()
}

fn get_connection() -> Connection {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();
    conn
}

// Creates a database with movies and related information for movie suggestions
// Movies: movie titles with associated release date and genre
// Actors: names of all actors
// Rankings: associates movie ID with IMDb ratings and number of ratings
// Crew (actors and directors): associates actors/directors with their movies
pub fn create_database() {

    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");

    // creates path if not existing
    create_dir_all(&path_buf);

    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS movies (
                  id              INTEGER PRIMARY KEY,
                  title            TEXT NOT NULL,
                  year            TEXT NOT NULL,
                  genre           TEXT
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS actors (
                  id              INTEGER PRIMARY KEY,
                  name            TEXT NOT NULL
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS directors (
                  id              INTEGER PRIMARY KEY,
                  name            TEXT NOT NULL
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS rankings (
                  id              INTEGER PRIMARY KEY,
                  movie_id        INTEGER,
                  score           TEXT NOT NULL,
                  number          TEXT NOT NULL,
                  FOREIGN KEY(movie_id) REFERENCES movies(id)
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS crew_a (
                  actor_id        INTEGER,
                  movie_id        INTEGER,
                  FOREIGN KEY(movie_id) REFERENCES movies(id),
                  FOREIGN KEY(actor_id) REFERENCES actors(id)
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS crew_d (
                  director_id        INTEGER,
                  movie_id        INTEGER,
                  FOREIGN KEY(movie_id) REFERENCES movies(id),
                  FOREIGN KEY(director_id) REFERENCES directors(id)
                  )",
                 &[])
        .unwrap();
}

// List of movies with their year of release
pub fn import_movie(title: &str, year: &str) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO movies (title, year) VALUES ('{}', '{}')",
                          title,
                          year),
                 &[])
        .unwrap();
}

// Genre information is added to the movie list
pub fn add_genres(title: &str, year: &str, genre: &str) {
    let conn = get_connection();

    conn.execute(&format!("UPDATE movies SET genre = '{}' WHERE title = '{}' AND year = '{}'",
                          genre,
                          title,
                          year),
                 &[])
        .unwrap();
}

// List of ratings with information of user ratings
pub fn add_rating(title: &str, year: &str, rank: &str, votes: &str) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO rankings (movie_id, score, number) VALUES ((SELECT id \
                           FROM movies WHERE title = '{}' AND year = '{}'), '{}', '{}')",
                          title,
                          year,
                          rank,
                          votes),
                 &[])
        .unwrap();
}

// List of all actors, independent of their movies
// Connection between actors and their movies outsourced to separate list
pub fn add_actor(name: &str, movies: Vec<Movie>) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO actors (name) VALUES ('{}')", &name),
                 &[])
        .unwrap();

    for m in movies {
        conn.execute(&format!("INSERT INTO crew_a (actor_id, movie_id) VALUES ((SELECT id FROM \
                               actors WHERE name = '{}'), (SELECT id FROM movies WHERE title = \
                               '{}' AND year = '{}'))",
                              name,
                              m.title,
                              m.year),
                     &[])
            .unwrap();
    }
}

// List of all directors, independent of their movies
// Connection between directors and their movies outsourced to separate list
pub fn add_director(name: &str, movies: Vec<Movie>) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO directors (name) VALUES ('{}')", &name),
                 &[])
        .unwrap();

    for m in movies {
        conn.execute(&format!("INSERT INTO crew_d (director_id, movie_id) VALUES ((SELECT id \
                               FROM directors WHERE name = '{}'), (SELECT id FROM movies WHERE \
                               title = '{}' AND year = '{}'))",
                              name,
                              m.title,
                              m.year),
                     &[])
            .unwrap();
    }
}
<commit_msg>Change documentation<commit_after>use rusqlite::Connection;
use std::env;
use std::fs::create_dir_all;

/// A struct to represent a movie to be attached to actors or directors
#[derive(Debug)]
pub struct Movie {
    pub title: String,
    pub year: String,
}

/// Checks if a database already exists
pub fn db_exists() -> bool {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");
    path_buf.as_path().exists()
}

/// Returns a connection to existing database
fn get_connection() -> Connection {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();
    conn
}

/// Creates a database with movies and related information for movie suggestions
/// Movies: movie titles with associated release date and genre
/// Actors: names of all actors
/// Rankings: associates movie ID with IMDb ratings and number of ratings
/// Crew (actors and directors): associates actors/directors with their movies
pub fn create_database() {

    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");

    // creates path if not existing
    create_dir_all(&path_buf);

    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS movies (
                  id              INTEGER PRIMARY KEY,
                  title            TEXT NOT NULL,
                  year            TEXT NOT NULL,
                  genre           TEXT
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS actors (
                  id              INTEGER PRIMARY KEY,
                  name            TEXT NOT NULL
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS directors (
                  id              INTEGER PRIMARY KEY,
                  name            TEXT NOT NULL
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS rankings (
                  id              INTEGER PRIMARY KEY,
                  movie_id        INTEGER,
                  score           TEXT NOT NULL,
                  number          TEXT NOT NULL,
                  FOREIGN KEY(movie_id) REFERENCES movies(id)
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS crew_a (
                  actor_id        INTEGER,
                  movie_id        INTEGER,
                  FOREIGN KEY(movie_id) REFERENCES movies(id),
                  FOREIGN KEY(actor_id) REFERENCES actors(id)
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS crew_d (
                  director_id        INTEGER,
                  movie_id        INTEGER,
                  FOREIGN KEY(movie_id) REFERENCES movies(id),
                  FOREIGN KEY(director_id) REFERENCES directors(id)
                  )",
                 &[])
        .unwrap();
}

/// List of movies with their year of release
pub fn import_movie(title: &str, year: &str) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO movies (title, year) VALUES ('{}', '{}')",
                          title,
                          year),
                 &[])
        .unwrap();
}

/// Genre information is added to the movie list
pub fn add_genres(title: &str, year: &str, genre: &str) {
    let conn = get_connection();

    conn.execute(&format!("UPDATE movies SET genre = '{}' WHERE title = '{}' AND year = '{}'",
                          genre,
                          title,
                          year),
                 &[])
        .unwrap();
}

/// List of ratings with information of user ratings
pub fn add_rating(title: &str, year: &str, rank: &str, votes: &str) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO rankings (movie_id, score, number) VALUES ((SELECT id \
                           FROM movies WHERE title = '{}' AND year = '{}'), '{}', '{}')",
                          title,
                          year,
                          rank,
                          votes),
                 &[])
        .unwrap();
}

/// List of all actors, independent of their movies
/// Connection between actors and their movies outsourced to separate list
pub fn add_actor(name: &str, movies: Vec<Movie>) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO actors (name) VALUES ('{}')", &name),
                 &[])
        .unwrap();

    for m in movies {
        conn.execute(&format!("INSERT INTO crew_a (actor_id, movie_id) VALUES ((SELECT id FROM \
                               actors WHERE name = '{}'), (SELECT id FROM movies WHERE title = \
                               '{}' AND year = '{}'))",
                              name,
                              m.title,
                              m.year),
                     &[])
            .unwrap();
    }
}

/// List of all directors, independent of their movies
/// Connection between directors and their movies outsourced to separate list
pub fn add_director(name: &str, movies: Vec<Movie>) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO directors (name) VALUES ('{}')", &name),
                 &[])
        .unwrap();

    for m in movies {
        conn.execute(&format!("INSERT INTO crew_d (director_id, movie_id) VALUES ((SELECT id \
                               FROM directors WHERE name = '{}'), (SELECT id FROM movies WHERE \
                               title = '{}' AND year = '{}'))",
                              name,
                              m.title,
                              m.year),
                     &[])
            .unwrap();
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use ast::{Function, Expr, Statement};
use rich_number::RichNumber;
use eval_tree_node::EvalTreeNode;
use prelude::setup_prelude;

const LOWEST_PRECEDENCE: u8 = 0;
const HIGHEST_PRECEDENCE: u8 = 9;
const FUNCTION_PRECEDENCE: u8 = HIGHEST_PRECEDENCE;

const FUNCTION_FIXITY: Fixity = Fixity::Prefix;

#[derive(PartialEq, Debug)]
pub enum EvalError {
    UndefinedSymbol(String),
    UnexpectedSymbols,
    ExpectedParams(usize),
    InvalidSymbolDefinition {
        expected_params: usize,
        actual_params: usize,
    },
}

pub type EvalResult = Result<ContextItem, EvalError>;

#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub enum Fixity {
    Prefix,
    Infix,
    Postfix,
}

#[derive(PartialEq, Debug, Clone)]
pub enum ContextItem {
    Number(RichNumber),
    Definition {
        // number from 0 to 9 where 9 is the highest precedence
        precedence: u8,
        fixity: Fixity,
        function: Function,
    },
    BuiltInMethod {
        precedence: u8,
        fixity: Fixity,
        params: usize,
        function: fn(Vec<ContextItem>) -> EvalResult,
    },
    Constant(String),
    Boolean(bool),
    Nothing,
}

impl ContextItem {
    /// Creates a Definition ContextItem from a function
    /// with the defaults assumed for all functions
    pub fn function_defaults(function: Function) -> ContextItem {
        ContextItem::Definition {
            precedence: FUNCTION_PRECEDENCE,
            fixity: FUNCTION_FIXITY,
            function: function,
        }
    }

    /// Creates a BuiltInMethod ContextItem from a function
    /// with the defaults assumed for all functions
    pub fn built_in_defaults(function: fn(Vec<ContextItem>) -> EvalResult, params: usize) -> ContextItem {
        ContextItem::BuiltInMethod {
            precedence: FUNCTION_PRECEDENCE,
            fixity: FUNCTION_FIXITY,
            params: params,
            function: function,
        }
    }

    pub fn unwrap_number(self) -> RichNumber {
        match self {
            ContextItem::Number(num) => num,
            _ => panic!("Expected to unwrap a Number"),
        }
    }

    pub fn unwrap_boolean(self) -> bool {
        match self {
            ContextItem::Boolean(value) => value,
            _ => panic!("Expected to unwrap a Boolean"),
        }
    }

    /// The most reliable way to get the precedence of any ContextItem
    /// ContextItems with no defined precedence return None
    pub fn resolve_precedence(&self) -> Option<&u8> {
        match *self {
            ContextItem::Definition { ref precedence, .. } => Some(precedence),
            ContextItem::BuiltInMethod { ref precedence, .. } => Some(precedence),
            _ => None,
        }
    }

    /// The most reliable way to get the fixity of any ContextItem
    /// ContextItems with no defined fixity return None
    pub fn resolve_fixity(&self) -> Option<Fixity> {
        match *self {
            ContextItem::Definition { fixity, .. } => Some(fixity),
            ContextItem::BuiltInMethod { fixity, .. } => Some(fixity),
            _ => None,
        }
    }

    /// The most reliable way to get the number of parameters of any ContextItem
    /// ContextItems with no defined number of parameters return None
    pub fn resolve_params(&self) -> Option<usize> {
        match *self {
            ContextItem::Definition {
                function: Function {
                    ref params,
                    ..
                },
                ..
            } => Some(params.len()),
            ContextItem::BuiltInMethod { ref params, .. } => Some(*params),
            _ => None,
        }
    }
}

pub type ConversionTable = HashMap<(String, String), Function>;

pub struct EvalContext {
    symbol_table: HashMap<String, ContextItem>,
    // (from unit, to unit) : Function definition
    conversion_table: ConversionTable,
}

impl EvalContext {
    pub fn new() -> EvalContext {
        EvalContext {
            symbol_table: HashMap::new(),
            conversion_table: HashMap::new(),
        }
    }

    pub fn prelude() -> EvalContext {
        let mut context = EvalContext::new();
        setup_prelude(&mut context);
        context
    }

    /// Gets a single value without evaluating it from the current context
    pub fn get(&self, name: &str) -> Option<ContextItem> {
        self.symbol_table.get(name).map(|item| (*item).clone())
    }

    /// Convenience method for setting a number in the context
    pub fn set_number(&mut self, name: &str, value: RichNumber) {
        self.set(name, ContextItem::Number(value))
    }

    /// Defines a function in the context
    pub fn define(
        &mut self,
        name: &str,
        fixity: Fixity,
        precedence: u8,
        function: Function,
    ) {
        debug_assert!(precedence >= LOWEST_PRECEDENCE && precedence <= HIGHEST_PRECEDENCE);

        self.set(name, ContextItem::Definition {
            fixity: fixity,
            precedence: precedence,
            function: function,
        });
    }

    /// Defines a function linked to actual code rather than an expression
    pub fn define_builtin_method(
        &mut self,
        name: &str,
        fixity: Fixity,
        precedence: u8,
        params: usize,
        function: fn(Vec<ContextItem>) -> EvalResult,
    ) {

        self.set(name, ContextItem::BuiltInMethod {
            fixity: fixity,
            precedence: precedence,
            params: params,
            function: function,
        });
    }

    /// Creates a constant value
    pub fn set_constant(&mut self, name: &str, value: String) {
        self.set(name, ContextItem::Constant(value));
    }

    /// Creates a boolean value
    pub fn set_boolean(&mut self, name: &str, value: bool) {
        self.set(name, ContextItem::Boolean(value));
    }

    /// Adds to the current context (silently replaces if already present)
    pub fn set(&mut self, name: &str, value: ContextItem) {
        self.symbol_table.insert(name.to_owned(), value);
    }

    pub fn apply(&mut self, statement: Statement) -> EvalResult {
        match statement {
            Statement::NamedFunction {name, definition} => {
                self.define(&name, FUNCTION_FIXITY, FUNCTION_PRECEDENCE, definition);
                Ok(ContextItem::Nothing)
            },
            Statement::AnonymousFunction(_) => Ok(ContextItem::Nothing),
            Statement::Assignment {name, value} => {
                let value = try!(self.evaluate(value));
                self.set(&name, value);
                Ok(ContextItem::Nothing)
            },
            Statement::Expression(value) => self.evaluate(value),
        }
    }

    fn evaluate(&mut self, expr: Expr) -> EvalResult {
        let root = try!(EvalTreeNode::from_expr(self, expr));

        println!("{:#?}", root);

        unimplemented!();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::*;
    use api::parse;
    use rich_number::RichNumber;

    #[test]
    fn get_set_in_context() {
        let mut context = EvalContext::new();
        assert_eq!(context.get("a"), None);

        let value = RichNumber::from(2f64);
        context.set_number("a", value.clone());
        assert_eq!(context.get("a").map(|n| n.unwrap_number()), Some(value));
    }

    #[test]
    fn define_variable() {
        let mut context = EvalContext::new();
        apply_string(&mut context, "x = 3");

        assert_eq!(context.get("x").map(|n| n.unwrap_number()), Some(RichNumber::from(3f64)));
    }

    #[test]
    fn basic_operators_and_precedence() {
        let result = apply_single("2 * 3 ** 2 - (100e-1 + -77.2) / 13").unwrap();
        assert_eq!(result.unwrap_number(), RichNumber::from(23.1692307692f64));
    }

    fn test_single_boolean(string: &str, expected: bool) {
        let result = apply_single(string).unwrap();
        assert_eq!(result.unwrap_boolean(), expected);
    }

    fn apply_single(string: &str) -> EvalResult {
        let mut context = EvalContext::new();
        let statement = parse_statement(string);

        context.apply(statement)
    }

    fn parse_statement(string: &str) -> Statement {
        let mut parsed = parse(string).unwrap();
        assert!(parsed.len() == 1, "parse_statement() was passed more than one statement");

        parsed.pop().unwrap()
    }

    fn apply_string(mut context: &mut EvalContext, string: &str) {
        apply_program(context, parse(string).unwrap());
    }

    fn apply_program(mut context: &mut EvalContext, program: Program) {
        for statement in program {
            context.apply(statement).expect("Error while applying statement");
        }
    }
}

<commit_msg>Proper types for units since units are usize, not string + a way to store the usize for a unit in the symbol table for lookup purposes<commit_after>use std::collections::HashMap;

use ast::{Function, Expr, Statement};
use rich_number::RichNumber;
use eval_tree_node::EvalTreeNode;
use prelude::setup_prelude;

const LOWEST_PRECEDENCE: u8 = 0;
const HIGHEST_PRECEDENCE: u8 = 9;
const FUNCTION_PRECEDENCE: u8 = HIGHEST_PRECEDENCE;

const FUNCTION_FIXITY: Fixity = Fixity::Prefix;

#[derive(PartialEq, Debug)]
pub enum EvalError {
    UndefinedSymbol(String),
    UnexpectedSymbols,
    ExpectedParams(usize),
    InvalidSymbolDefinition {
        expected_params: usize,
        actual_params: usize,
    },
}

pub type EvalResult = Result<ContextItem, EvalError>;

#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub enum Fixity {
    Prefix,
    Infix,
    Postfix,
}

#[derive(PartialEq, Debug, Clone)]
pub enum ContextItem {
    Number(RichNumber),
    Definition {
        // number from 0 to 9 where 9 is the highest precedence
        precedence: u8,
        fixity: Fixity,
        function: Function,
    },
    BuiltInMethod {
        precedence: u8,
        fixity: Fixity,
        params: usize,
        function: fn(Vec<ContextItem>) -> EvalResult,
    },
    Unit(usize),
    Constant(String),
    Boolean(bool),
    Nothing,
}

impl ContextItem {
    /// Creates a Definition ContextItem from a function
    /// with the defaults assumed for all functions
    pub fn function_defaults(function: Function) -> ContextItem {
        ContextItem::Definition {
            precedence: FUNCTION_PRECEDENCE,
            fixity: FUNCTION_FIXITY,
            function: function,
        }
    }

    /// Creates a BuiltInMethod ContextItem from a function
    /// with the defaults assumed for all functions
    pub fn built_in_defaults(function: fn(Vec<ContextItem>) -> EvalResult, params: usize) -> ContextItem {
        ContextItem::BuiltInMethod {
            precedence: FUNCTION_PRECEDENCE,
            fixity: FUNCTION_FIXITY,
            params: params,
            function: function,
        }
    }

    pub fn unwrap_number(self) -> RichNumber {
        match self {
            ContextItem::Number(num) => num,
            _ => panic!("Expected to unwrap a Number"),
        }
    }

    pub fn unwrap_boolean(self) -> bool {
        match self {
            ContextItem::Boolean(value) => value,
            _ => panic!("Expected to unwrap a Boolean"),
        }
    }

    /// The most reliable way to get the precedence of any ContextItem
    /// ContextItems with no defined precedence return None
    pub fn resolve_precedence(&self) -> Option<&u8> {
        match *self {
            ContextItem::Definition { ref precedence, .. } => Some(precedence),
            ContextItem::BuiltInMethod { ref precedence, .. } => Some(precedence),
            _ => None,
        }
    }

    /// The most reliable way to get the fixity of any ContextItem
    /// ContextItems with no defined fixity return None
    pub fn resolve_fixity(&self) -> Option<Fixity> {
        match *self {
            ContextItem::Definition { fixity, .. } => Some(fixity),
            ContextItem::BuiltInMethod { fixity, .. } => Some(fixity),
            _ => None,
        }
    }

    /// The most reliable way to get the number of parameters of any ContextItem
    /// ContextItems with no defined number of parameters return None
    pub fn resolve_params(&self) -> Option<usize> {
        match *self {
            ContextItem::Definition {
                function: Function {
                    ref params,
                    ..
                },
                ..
            } => Some(params.len()),
            ContextItem::BuiltInMethod { ref params, .. } => Some(*params),
            _ => None,
        }
    }
}

// (from unit, to unit) : Function definition
pub type ConversionTable = HashMap<(usize, usize), Function>;

pub struct EvalContext {
    symbol_table: HashMap<String, ContextItem>,
    conversion_table: ConversionTable,
}

impl EvalContext {
    pub fn new() -> EvalContext {
        EvalContext {
            symbol_table: HashMap::new(),
            conversion_table: HashMap::new(),
        }
    }

    pub fn prelude() -> EvalContext {
        let mut context = EvalContext::new();
        setup_prelude(&mut context);
        context
    }

    /// Gets a single value without evaluating it from the current context
    pub fn get(&self, name: &str) -> Option<ContextItem> {
        self.symbol_table.get(name).map(|item| (*item).clone())
    }

    /// Convenience method for setting a number in the context
    pub fn set_number(&mut self, name: &str, value: RichNumber) {
        self.set(name, ContextItem::Number(value))
    }

    /// Defines a function in the context
    pub fn define(
        &mut self,
        name: &str,
        fixity: Fixity,
        precedence: u8,
        function: Function,
    ) {
        debug_assert!(precedence >= LOWEST_PRECEDENCE && precedence <= HIGHEST_PRECEDENCE);

        self.set(name, ContextItem::Definition {
            fixity: fixity,
            precedence: precedence,
            function: function,
        });
    }

    /// Defines a function linked to actual code rather than an expression
    pub fn define_builtin_method(
        &mut self,
        name: &str,
        fixity: Fixity,
        precedence: u8,
        params: usize,
        function: fn(Vec<ContextItem>) -> EvalResult,
    ) {

        self.set(name, ContextItem::BuiltInMethod {
            fixity: fixity,
            precedence: precedence,
            params: params,
            function: function,
        });
    }

    /// Creates a constant value
    pub fn set_constant(&mut self, name: &str, value: String) {
        self.set(name, ContextItem::Constant(value));
    }

    /// Creates a boolean value
    pub fn set_boolean(&mut self, name: &str, value: bool) {
        self.set(name, ContextItem::Boolean(value));
    }

    /// Adds to the current context (silently replaces if already present)
    pub fn set(&mut self, name: &str, value: ContextItem) {
        self.symbol_table.insert(name.to_owned(), value);
    }

    pub fn apply(&mut self, statement: Statement) -> EvalResult {
        match statement {
            Statement::NamedFunction {name, definition} => {
                self.define(&name, FUNCTION_FIXITY, FUNCTION_PRECEDENCE, definition);
                Ok(ContextItem::Nothing)
            },
            Statement::AnonymousFunction(_) => Ok(ContextItem::Nothing),
            Statement::Assignment {name, value} => {
                let value = try!(self.evaluate(value));
                self.set(&name, value);
                Ok(ContextItem::Nothing)
            },
            Statement::Expression(value) => self.evaluate(value),
        }
    }

    fn evaluate(&mut self, expr: Expr) -> EvalResult {
        let root = try!(EvalTreeNode::from_expr(self, expr));

        println!("{:#?}", root);

        unimplemented!();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::*;
    use api::parse;
    use rich_number::RichNumber;

    #[test]
    fn get_set_in_context() {
        let mut context = EvalContext::new();
        assert_eq!(context.get("a"), None);

        let value = RichNumber::from(2f64);
        context.set_number("a", value.clone());
        assert_eq!(context.get("a").map(|n| n.unwrap_number()), Some(value));
    }

    #[test]
    fn define_variable() {
        let mut context = EvalContext::new();
        apply_string(&mut context, "x = 3");

        assert_eq!(context.get("x").map(|n| n.unwrap_number()), Some(RichNumber::from(3f64)));
    }

    #[test]
    fn basic_operators_and_precedence() {
        let result = apply_single("2 * 3 ** 2 - (100e-1 + -77.2) / 13").unwrap();
        assert_eq!(result.unwrap_number(), RichNumber::from(23.1692307692f64));
    }

    fn test_single_boolean(string: &str, expected: bool) {
        let result = apply_single(string).unwrap();
        assert_eq!(result.unwrap_boolean(), expected);
    }

    fn apply_single(string: &str) -> EvalResult {
        let mut context = EvalContext::new();
        let statement = parse_statement(string);

        context.apply(statement)
    }

    fn parse_statement(string: &str) -> Statement {
        let mut parsed = parse(string).unwrap();
        assert!(parsed.len() == 1, "parse_statement() was passed more than one statement");

        parsed.pop().unwrap()
    }

    fn apply_string(mut context: &mut EvalContext, string: &str) {
        apply_program(context, parse(string).unwrap());
    }

    fn apply_program(mut context: &mut EvalContext, program: Program) {
        for statement in program {
            context.apply(statement).expect("Error while applying statement");
        }
    }
}

<|endoftext|>


<commit_before>use bullet::BulletDrawable;
use cgmath;
use cgmath::{Angle, Deg, Matrix4, Point2, Point3, Vector3};
use character::CharacterDrawable;
use game::constants::{RESOLUTION_Y, TERRAIN_OBJECTS, TILE_WIDTH, TILES_PCS_H, TILES_PCS_W, VIEW_DISTANCE};
use gfx::{Factory, format::Rgba8, handle::ShaderResourceView, Resources, texture};
use gfx_app::mouse_controls::MouseInputState;
use graphics::orientation::Orientation;
use image;
use shaders::{Position, Projection};
use std::io::Cursor;
use terrain_object::TerrainObjectDrawable;
use zombie::ZombieDrawable;

pub mod camera;
pub mod orientation;

#[derive(Debug)]
pub struct DeltaTime(pub f64);

#[derive(Debug, Clone)]
pub struct Dimensions {
  pub window_width: f32,
  pub window_height: f32,
  pub hidpi_factor: f32,
}

impl Dimensions {
  pub fn new(window_width: f32, window_height: f32, hidpi_factor: f32) -> Dimensions {
    Dimensions {
      window_width,
      window_height,
      hidpi_factor,
    }
  }

  pub fn get_view_matrix() -> Matrix4<f32> {
    Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    )
  }

  pub fn world_to_projection(&self, input: &camera::CameraInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, input.distance),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.window_width / self.window_height;
    Projection {
      model: view.into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(75.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2::new(point.x, RESOLUTION_Y as f32 - point.y)
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> u32 {
  let theta = cgmath::Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let angle = match theta {
    Deg(i) => i
  };
  let a = angle.floor() as i32;
  if a < 0 { (360 + a) as u32 } else { a as u32 }
}

pub fn direction_movement(direction: u32) -> Point2<f32> {
  let f_direction = direction as f32;
  Point2::new((Angle::cos(Deg(f_direction)) * 100.0).round() / 100.0,
              (Angle::sin(Deg(f_direction)) * 100.0).round() / 100.0)
}

pub fn get_orientation(angle_in_degrees: u32) -> Orientation {
  match angle_in_degrees {
    345 ... 360 | 0 ... 22 => Orientation::Right,
    23 ... 68 => Orientation::UpRight,
    69 ... 114 => Orientation::Up,
    115 ... 160 => Orientation::UpLeft,
    161 ... 206 => Orientation::Left,
    207 ... 252 => Orientation::DownLeft,
    253 ... 298 => Orientation::Down,
    299 ... 344 => Orientation::DownRight,
    _ => unreachable!("Invalid orientation")
  }
}

pub fn get_orientation_from_center(mouse_input: &MouseInputState, dim: &Dimensions) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2::new(dim.window_width / 2.0 * dim.hidpi_factor, dim.window_height / 2.0 * dim.hidpi_factor);
    let dir = direction(start_point, flip_y_axel(end_point_gl));
    get_orientation(dir)
  } else {
    Orientation::Right
  }
}

pub fn overlaps(a: Position, b: Position, width: f32, height: f32) -> bool {
  a.position[0] < b.position[0] + width &&
    a.position[0] + width > b.position[0] &&
    a.position[1] < b.position[1] + height &&
    a.position[1] + a.position[1] > b.position[1]
}

fn is_within_map_borders(point: Point2<f32>) -> bool {
  point.x > 0.0 && point.x < 63.0 && point.y > 0.0 && point.y < 63.0
}

pub fn can_move(screen_pos: Position) -> bool {
  let tile_width = TILE_WIDTH;
  let x_coord = screen_pos.position[0];
  let y_coord = screen_pos.position[1];
  let point = Point2::new(
    (x_coord / tile_width + y_coord / tile_width).round() + 31.0,
    (y_coord / tile_width - x_coord / tile_width).round() + 32.0);
  is_within_map_borders(point)
}

pub fn is_not_terrain_object(pos: Point2<usize>) -> bool {
  !TERRAIN_OBJECTS.iter().any(|e| (e[0] == pos.x) && (e[1] == pos.y))
}

fn is_map_tile(pos: Point2<usize>) -> bool {
  pos.x >= 1usize && pos.y >= 1usize && pos.x < TILES_PCS_W && pos.y < TILES_PCS_H
}

pub fn can_move_to_tile(screen_pos: Position) -> bool {
  let pos = coords_to_tile(screen_pos);
  is_not_terrain_object(pos) && is_map_tile(pos)
}

pub fn coords_to_tile(position: Position) -> Point2<usize> {
  let tile_width = TILE_WIDTH;
  let pos = Point2 {
    x: -position.position[0],
    y: position.position[1] + 1500.0,
  };
  Point2 {
    x : (pos.x / tile_width + (pos.y / tile_width)) as usize,
    y: (pos.y / tile_width - (pos.x / tile_width)) as usize,
  }
}

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> ShaderResourceView<R, [f32; 4]> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  match factory.create_texture_immutable_u8::<Rgba8>(kind, texture::Mipmap::Provided, &[&img]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub enum Drawables {
  Bullet(BulletDrawable),
  Character(CharacterDrawable),
  TerrainHouse(TerrainObjectDrawable),
  TerrainTree(TerrainObjectDrawable),
  Zombie(ZombieDrawable),
}

pub trait DrawOrder {
  fn get_zindex(&self) -> (f32, Drawables, &Self);
}
<commit_msg>Add "load_raw_texture" function<commit_after>use bullet::BulletDrawable;
use cgmath;
use cgmath::{Angle, Deg, Matrix4, Point2, Point3, Vector3};
use character::CharacterDrawable;
use game::constants::{RESOLUTION_Y, TERRAIN_OBJECTS, TILE_WIDTH, TILES_PCS_H, TILES_PCS_W, VIEW_DISTANCE};
use gfx::{Factory, format::Rgba8, handle::ShaderResourceView, Resources, texture};
use gfx_app::mouse_controls::MouseInputState;
use graphics::orientation::Orientation;
use image;
use shaders::{Position, Projection};
use std::io::Cursor;
use terrain_object::TerrainObjectDrawable;
use zombie::ZombieDrawable;
use gfx_app::ColorFormat;

pub mod camera;
pub mod orientation;

#[derive(Debug)]
pub struct DeltaTime(pub f64);

#[derive(Debug, Clone)]
pub struct Dimensions {
  pub window_width: f32,
  pub window_height: f32,
  pub hidpi_factor: f32,
}

impl Dimensions {
  pub fn new(window_width: f32, window_height: f32, hidpi_factor: f32) -> Dimensions {
    Dimensions {
      window_width,
      window_height,
      hidpi_factor,
    }
  }

  pub fn get_view_matrix() -> Matrix4<f32> {
    Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    )
  }

  pub fn world_to_projection(&self, input: &camera::CameraInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, input.distance),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.window_width / self.window_height;
    Projection {
      model: view.into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(75.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

pub fn flip_y_axel(point: Point2<f32>) -> Point2<f32> {
  Point2::new(point.x, RESOLUTION_Y as f32 - point.y)
}

pub fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> u32 {
  let theta = cgmath::Angle::atan2(end_point.y - start_point.y, end_point.x - start_point.x);
  let angle = match theta {
    Deg(i) => i
  };
  let a = angle.floor() as i32;
  if a < 0 { (360 + a) as u32 } else { a as u32 }
}

pub fn direction_movement(direction: u32) -> Point2<f32> {
  let f_direction = direction as f32;
  Point2::new((Angle::cos(Deg(f_direction)) * 100.0).round() / 100.0,
              (Angle::sin(Deg(f_direction)) * 100.0).round() / 100.0)
}

pub fn get_orientation(angle_in_degrees: u32) -> Orientation {
  match angle_in_degrees {
    345 ... 360 | 0 ... 22 => Orientation::Right,
    23 ... 68 => Orientation::UpRight,
    69 ... 114 => Orientation::Up,
    115 ... 160 => Orientation::UpLeft,
    161 ... 206 => Orientation::Left,
    207 ... 252 => Orientation::DownLeft,
    253 ... 298 => Orientation::Down,
    299 ... 344 => Orientation::DownRight,
    _ => unreachable!("Invalid orientation")
  }
}

pub fn get_orientation_from_center(mouse_input: &MouseInputState, dim: &Dimensions) -> Orientation {
  if let Some(end_point_gl) = mouse_input.left_click_point {
    let start_point = Point2::new(dim.window_width / 2.0 * dim.hidpi_factor, dim.window_height / 2.0 * dim.hidpi_factor);
    let dir = direction(start_point, flip_y_axel(end_point_gl));
    get_orientation(dir)
  } else {
    Orientation::Right
  }
}

pub fn overlaps(a: Position, b: Position, width: f32, height: f32) -> bool {
  a.position[0] < b.position[0] + width &&
    a.position[0] + width > b.position[0] &&
    a.position[1] < b.position[1] + height &&
    a.position[1] + a.position[1] > b.position[1]
}

fn is_within_map_borders(point: Point2<f32>) -> bool {
  point.x > 0.0 && point.x < 63.0 && point.y > 0.0 && point.y < 63.0
}

pub fn can_move(screen_pos: Position) -> bool {
  let tile_width = TILE_WIDTH;
  let x_coord = screen_pos.position[0];
  let y_coord = screen_pos.position[1];
  let point = Point2::new(
    (x_coord / tile_width + y_coord / tile_width).round() + 31.0,
    (y_coord / tile_width - x_coord / tile_width).round() + 32.0);
  is_within_map_borders(point)
}

pub fn is_not_terrain_object(pos: Point2<usize>) -> bool {
  !TERRAIN_OBJECTS.iter().any(|e| (e[0] == pos.x) && (e[1] == pos.y))
}

fn is_map_tile(pos: Point2<usize>) -> bool {
  pos.x >= 1usize && pos.y >= 1usize && pos.x < TILES_PCS_W && pos.y < TILES_PCS_H
}

pub fn can_move_to_tile(screen_pos: Position) -> bool {
  let pos = coords_to_tile(screen_pos);
  is_not_terrain_object(pos) && is_map_tile(pos)
}

pub fn coords_to_tile(position: Position) -> Point2<usize> {
  let tile_width = TILE_WIDTH;
  let pos = Point2 {
    x: -position.position[0],
    y: position.position[1] + 1500.0,
  };
  Point2 {
    x : (pos.x / tile_width + (pos.y / tile_width)) as usize,
    y: (pos.y / tile_width - (pos.x / tile_width)) as usize,
  }
}

pub fn load_texture<R, F>(factory: &mut F, data: &[u8]) -> ShaderResourceView<R, [f32; 4]> where R: Resources, F: Factory<R> {
  let img = image::load(Cursor::new(data), image::PNG).unwrap().to_rgba();
  let (width, height) = img.dimensions();
  let kind = texture::Kind::D2(width as texture::Size, height as texture::Size, texture::AaMode::Single);
  match factory.create_texture_immutable_u8::<Rgba8>(kind, texture::Mipmap::Provided, &[&img]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub fn load_raw_texture<R, F>(factory: &mut F, data: &[u8], size: Point2<i32>) -> ShaderResourceView<R, [f32; 4]>
                      where R: Resources, F: Factory<R> {
  let kind = texture::Kind::D2(size.x as texture::Size, size.y as texture::Size, texture::AaMode::Single);
  let mipmap = texture::Mipmap::Provided;
  match factory
    .create_texture_immutable_u8::<ColorFormat>(kind, mipmap, &[data]) {
    Ok(val) => val.1,
    Err(e) => panic!("Couldn't load texture {:?}", e)
  }
}

pub enum Drawables {
  Bullet(BulletDrawable),
  Character(CharacterDrawable),
  TerrainHouse(TerrainObjectDrawable),
  TerrainTree(TerrainObjectDrawable),
  Zombie(ZombieDrawable),
}

pub trait DrawOrder {
  fn get_zindex(&self) -> (f32, Drawables, &Self);
}
<|endoftext|>


<commit_before>use hyper::version::HttpVersion;

pub trait ValidateHttpVersion {
    /// Check the HttpVersion is at least 1.1 to enable chunks download.
    ///
    fn greater_than_http_11(&self) -> bool;
}

impl ValidateHttpVersion for HttpVersion {
    /// Check the given HttpVersion.
    ///
    /// This version should be at least 1.1 to allow chunks downloading.
    ///
    fn greater_than_http_11(&self) -> bool {
        self >= &HttpVersion::Http11
    }
}
<commit_msg>Update comment<commit_after>use hyper::version::HttpVersion;

/// Trait used to check HTTP Version
///
/// This trait is used to validate that a given HTTP Version match specific need.
pub trait ValidateHttpVersion {
    /// Validate that the current HttpVersion is at least 1.1 to be able to download chunks.
    fn greater_than_http_11(&self) -> bool;
}

impl ValidateHttpVersion for HttpVersion {
    /// Check the given HttpVersion.
    ///
    /// This version should be at least 1.1 to allow chunks downloading.
    fn greater_than_http_11(&self) -> bool {
        self >= &HttpVersion::Http11
    }
}
<|endoftext|>


<commit_before>use super::*;

use buffer::*;

fn execute_and_check(program: &[Instruction], input: &str, exp_output: &[u8]) {
    let mut input = IBuffer::from_str(input);
    let mut output = Vec::new();
    Program { instructions: program.to_vec() }.interp(&mut input, &mut output);
    assert_eq!(exp_output, output.as_slice());
}

#[test]
fn output_zero() {
    execute_and_check(&[Output], "", &[0]);
}

#[test]
fn inc_by_one() {
    execute_and_check(&[Inc(1), Output], "", &[1]);
}

#[test]
fn inc_by_two() {
    execute_and_check(&[Inc(2), Output], "", &[2]);
}

#[test]
fn inc_by_one_and_by_one() {
    execute_and_check(&[Inc(1), Inc(1), Output], "", &[2]);
}

#[test]
fn inc_wrapping() {
    execute_and_check(&[Inc(255), Inc(1), Output], "", &[0]);
}

#[test]
fn dec_by_one() {
    execute_and_check(&[Inc(2), Dec(1), Output], "", &[1]);
}

#[test]
fn dec_by_two() {
    execute_and_check(&[Inc(2), Dec(2), Output], "", &[0]);
}

#[test]
fn dec_by_one_and_by_one() {
    execute_and_check(&[Inc(3), Dec(1), Dec(1), Output], "", &[1]);
}

#[test]
fn dec_wrapping() {
    execute_and_check(&[Dec(1), Inc(2), Output], "", &[1]);
}

#[test]
fn move_right() {
    execute_and_check(&[Inc(2), MoveRight(1), Inc(1), Output], "", &[1]);
}

#[test]
fn move_left() {
    execute_and_check(&[Inc(2), MoveRight(1), Inc(1), Output, MoveLeft(1), Output],
                      "",
                      &[1, 2]);
}

#[test]
fn move_right_2() {
    execute_and_check(&[Inc(3),
                        MoveRight(1),
                        MoveRight(1),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        Output,
                        MoveLeft(1)],
                      "",
                      &[1, 0]);
}

#[test]
fn move_right_2_at_once() {
    execute_and_check(&[Inc(3),
                        MoveRight(2),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        Output,
                        MoveLeft(1)],
                      "",
                      &[1, 0]);
}

#[test]
fn move_left_2() {
    execute_and_check(&[Inc(2),
                        MoveRight(2),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        MoveLeft(1),
                        Output],
                      "",
                      &[1, 2]);
}

#[test]
fn move_left_2_at_once() {
    execute_and_check(&[Inc(2),
                        MoveRight(2),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        MoveLeft(1),
                        Output],
                      "",
                      &[1, 2]);
}

#[test]
fn input() {
    execute_and_check(&[Input, Output, Input, Output], "a1", &[b'a', b'1']);
}

#[test]
fn loop_set_zero() {
    execute_and_check(&[Inc(100), LoopEntry(3), Dec(1), LoopExit(1), Output],
                      "",
                      &[0]);
}

#[test]
fn loop_move_value() {
    execute_and_check(&[Inc(100),
                        LoopEntry(6),
                        Dec(1),
                        MoveRight(3),
                        Inc(1),
                        MoveLeft(3),
                        LoopExit(1),
                        Output,
                        MoveRight(3),
                        Output],
                      "",
                      &[0, 100]);
}

#[test]
fn loop_find_zero_left() {
    execute_and_check(&[MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        LoopEntry(8),
                        MoveLeft(1),
                        LoopExit(6),
                        Output],
                      "",
                      &[0]);
}

#[test]
fn loop_find_zero_right() {
    execute_and_check(&[MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        LoopEntry(8),
                        MoveLeft(1),
                        LoopExit(6),
                        Output,
                        Inc(9),
                        Output,
                        LoopEntry(14),
                        MoveRight(1),
                        LoopExit(12),
                        Output],
                      "",
                      &[0, 9, 0]);
}
<commit_msg>test loop begin<commit_after>use super::*;

use buffer::*;

fn execute_and_check(program: &[Instruction], input: &str, exp_output: &[u8]) {
    let mut input = IBuffer::from_str(input);
    let mut output = Vec::new();
    Program { instructions: program.to_vec() }.interp(&mut input, &mut output);
    assert_eq!(exp_output, output.as_slice());
}

#[test]
fn output_zero() {
    execute_and_check(&[Output], "", &[0]);
}

#[test]
fn inc_by_one() {
    execute_and_check(&[Inc(1), Output], "", &[1]);
}

#[test]
fn inc_by_two() {
    execute_and_check(&[Inc(2), Output], "", &[2]);
}

#[test]
fn inc_by_one_and_by_one() {
    execute_and_check(&[Inc(1), Inc(1), Output], "", &[2]);
}

#[test]
fn inc_wrapping() {
    execute_and_check(&[Inc(255), Inc(1), Output], "", &[0]);
}

#[test]
fn dec_by_one() {
    execute_and_check(&[Inc(2), Dec(1), Output], "", &[1]);
}

#[test]
fn dec_by_two() {
    execute_and_check(&[Inc(2), Dec(2), Output], "", &[0]);
}

#[test]
fn dec_by_one_and_by_one() {
    execute_and_check(&[Inc(3), Dec(1), Dec(1), Output], "", &[1]);
}

#[test]
fn dec_wrapping() {
    execute_and_check(&[Dec(1), Inc(2), Output], "", &[1]);
}

#[test]
fn move_right() {
    execute_and_check(&[Inc(2), MoveRight(1), Inc(1), Output], "", &[1]);
}

#[test]
fn move_left() {
    execute_and_check(&[Inc(2), MoveRight(1), Inc(1), Output, MoveLeft(1), Output],
                      "",
                      &[1, 2]);
}

#[test]
fn move_right_2() {
    execute_and_check(&[Inc(3),
                        MoveRight(1),
                        MoveRight(1),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        Output,
                        MoveLeft(1)],
                      "",
                      &[1, 0]);
}

#[test]
fn move_right_2_at_once() {
    execute_and_check(&[Inc(3),
                        MoveRight(2),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        Output,
                        MoveLeft(1)],
                      "",
                      &[1, 0]);
}

#[test]
fn move_left_2() {
    execute_and_check(&[Inc(2),
                        MoveRight(2),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        MoveLeft(1),
                        Output],
                      "",
                      &[1, 2]);
}

#[test]
fn move_left_2_at_once() {
    execute_and_check(&[Inc(2),
                        MoveRight(2),
                        Inc(1),
                        Output,
                        MoveLeft(1),
                        MoveLeft(1),
                        Output],
                      "",
                      &[1, 2]);
}

#[test]
fn input() {
    execute_and_check(&[Input, Output, Input, Output], "a1", &[b'a', b'1']);
}

#[test]
fn skip_loop() {
    execute_and_check(&[MoveRight(1),
                        Inc(1),
                        MoveLeft(1),
                        LoopEntry(5),
                        Inc(1),
                        LoopExit(3),
                        Output],
                      "",
                      &[0]);
}

#[test]
fn loop_set_zero() {
    execute_and_check(&[Inc(100), LoopEntry(3), Dec(1), LoopExit(1), Output],
                      "",
                      &[0]);
}

#[test]
fn loop_move_value() {
    execute_and_check(&[Inc(100),
                        LoopEntry(6),
                        Dec(1),
                        MoveRight(3),
                        Inc(1),
                        MoveLeft(3),
                        LoopExit(1),
                        Output,
                        MoveRight(3),
                        Output],
                      "",
                      &[0, 100]);
}

#[test]
fn loop_find_zero_left() {
    execute_and_check(&[MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        LoopEntry(8),
                        MoveLeft(1),
                        LoopExit(6),
                        Output],
                      "",
                      &[0]);
}

#[test]
fn loop_find_zero_right() {
    execute_and_check(&[MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        MoveRight(1),
                        Inc(1),
                        LoopEntry(8),
                        MoveLeft(1),
                        LoopExit(6),
                        Output,
                        Inc(9),
                        Output,
                        LoopEntry(14),
                        MoveRight(1),
                        LoopExit(12),
                        Output],
                      "",
                      &[0, 9, 0]);
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;
use std::io::{self, Cursor, Read};
use std::marker::PhantomData;
use std::mem;

use crate::color::ColorType;
use crate::image::ImageDecoder;
use crate::error::{ImageError, ImageResult};

/// JPEG decoder
pub struct JpegDecoder<R> {
    decoder: jpeg::Decoder<R>,
    metadata: jpeg::ImageInfo,
}

impl<R: Read> JpegDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
        let mut decoder = jpeg::Decoder::new(r);

        decoder.read_info().map_err(ImageError::from_jpeg)?;
        let mut metadata = decoder.info().unwrap();

        // We convert CMYK data to RGB before returning it to the user.
        if metadata.pixel_format == jpeg::PixelFormat::CMYK32 {
            metadata.pixel_format = jpeg::PixelFormat::RGB24;
        }

        Ok(JpegDecoder {
            decoder,
            metadata,
        })
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for JpegReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> {
    type Reader = JpegReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        (u32::from(self.metadata.width), u32::from(self.metadata.height))
    }

    fn color_type(&self) -> ColorType {
        ColorType::from_jpeg(self.metadata.pixel_format)
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        Ok(JpegReader(Cursor::new(data), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));

        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        buf.copy_from_slice(&data);
        Ok(())
    }
}

pub fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let count = input.len()/4;
    let mut output = vec![0; 3*count];

    let in_pixels = input[..4*count].chunks_exact(4);
    let out_pixels = output[..3*count].chunks_exact_mut(3);

    let inv = 1.0/255.0;
    for (pixel, outp) in in_pixels.zip(out_pixels) {
        let c = 255 - u16::from(pixel[0]);
        let m = 255 - u16::from(pixel[1]);
        let y = 255 - u16::from(pixel[2]);
        let k = 255 - u16::from(pixel[3]);

        // CMY -> RGB
        let r = (k * c) as f32 * inv;
        let g = (k * m) as f32 * inv;
        let b = (k * y) as f32 * inv;

        outp[0] = r as u8;
        outp[1] = g as u8;
        outp[2] = b as u8;
    }

    output
}

impl ColorType {
    fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType {
        use jpeg::PixelFormat::*;
        match pixel_format {
            L8 => ColorType::L8,
            RGB24 => ColorType::Rgb8,
            CMYK32 => panic!(),
        }
    }
}

impl ImageError {
    fn from_jpeg(err: jpeg::Error) -> ImageError {
        use jpeg::Error::*;
        match err {
            Format(desc) => ImageError::FormatError(desc),
            Unsupported(desc) => ImageError::UnsupportedError(format!("{:?}", desc)),
            Io(err) => ImageError::IoError(err),
            Internal(err) => ImageError::FormatError(err.to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::cmyk_to_rgb;

    #[test]
    fn cymk_to_rgb_correct() {
        // Based on R = 255 * (1-C/255) * (1-K/255)
        for c in 0..255 {
            for k in 0..255 {
                let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0;
                let r_u8 = r as u8;
                let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0];
                let convert_g = cmyk_to_rgb(&[0, c, 0, k])[0];
                let convert_b = cmyk_to_rgb(&[0, 0, c, k])[0];

                assert_eq!(convert_r, r_u8,
                           "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_r, r_u8);
                assert_eq!(convert_g, r_u8,
                           "m = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_g, r_u8);
                assert_eq!(convert_b, r_u8,
                           "y = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_b, r_u8);
            }
        }
    }

    fn single_pix_correct(cymk_pix: [u8; 4], rgb_pix_true: [u8; 3]) {
        let rgb_pix = cmyk_to_rgb(&cymk_pix);
        assert_eq!(rgb_pix[0], rgb_pix_true[0]);
        assert_eq!(rgb_pix[1], rgb_pix_true[1]);
        assert_eq!(rgb_pix[2], rgb_pix_true[2]);
    }

    #[test]
    fn test_grey_colors() {
        single_pix_correct([0, 0, 0, 0], [255, 255, 255]);
        single_pix_correct([0, 0, 0, 254], [1, 1, 1]);
        single_pix_correct([0, 0, 0, 253], [2, 2, 2]);
    }

}

<commit_msg>Further optimized version.<commit_after>use std::convert::TryFrom;
use std::io::{self, Cursor, Read};
use std::marker::PhantomData;
use std::mem;

use crate::color::ColorType;
use crate::image::ImageDecoder;
use crate::error::{ImageError, ImageResult};

/// JPEG decoder
pub struct JpegDecoder<R> {
    decoder: jpeg::Decoder<R>,
    metadata: jpeg::ImageInfo,
}

impl<R: Read> JpegDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
        let mut decoder = jpeg::Decoder::new(r);

        decoder.read_info().map_err(ImageError::from_jpeg)?;
        let mut metadata = decoder.info().unwrap();

        // We convert CMYK data to RGB before returning it to the user.
        if metadata.pixel_format == jpeg::PixelFormat::CMYK32 {
            metadata.pixel_format = jpeg::PixelFormat::RGB24;
        }

        Ok(JpegDecoder {
            decoder,
            metadata,
        })
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for JpegReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> {
    type Reader = JpegReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        (u32::from(self.metadata.width), u32::from(self.metadata.height))
    }

    fn color_type(&self) -> ColorType {
        ColorType::from_jpeg(self.metadata.pixel_format)
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        Ok(JpegReader(Cursor::new(data), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));

        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        buf.copy_from_slice(&data);
        Ok(())
    }
}

pub fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let count = input.len()/4;
    let mut output = vec![0; 3*count];

    let in_pixels = input[..4*count].chunks_exact(4);
    let out_pixels = output[..3*count].chunks_exact_mut(3);

    for (pixel, outp) in in_pixels.zip(out_pixels) {
        let c = 255 - u16::from(pixel[0]);
        let m = 255 - u16::from(pixel[1]);
        let y = 255 - u16::from(pixel[2]);
        let k = 255 - u16::from(pixel[3]);
        // CMY -> RGB
        let r = (k * c) / 255;
        let g = (k * m) / 255;
        let b = (k * y) / 255;

        outp[0] = r as u8;
        outp[1] = g as u8;
        outp[2] = b as u8;
    }

    output
}

impl ColorType {
    fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType {
        use jpeg::PixelFormat::*;
        match pixel_format {
            L8 => ColorType::L8,
            RGB24 => ColorType::Rgb8,
            CMYK32 => panic!(),
        }
    }
}

impl ImageError {
    fn from_jpeg(err: jpeg::Error) -> ImageError {
        use jpeg::Error::*;
        match err {
            Format(desc) => ImageError::FormatError(desc),
            Unsupported(desc) => ImageError::UnsupportedError(format!("{:?}", desc)),
            Io(err) => ImageError::IoError(err),
            Internal(err) => ImageError::FormatError(err.to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::cmyk_to_rgb;

    #[test]
    fn cymk_to_rgb_correct() {
        // Based on R = 255 * (1-C/255) * (1-K/255)
        for c in 0..255 {
            for k in 0..255 {
                let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0;
                let r_u8 = r as u8;
                let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0];
                let convert_g = cmyk_to_rgb(&[0, c, 0, k])[0];
                let convert_b = cmyk_to_rgb(&[0, 0, c, k])[0];

                assert_eq!(convert_r, r_u8,
                           "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_r, r_u8);
                assert_eq!(convert_g, r_u8,
                           "m = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_g, r_u8);
                assert_eq!(convert_b, r_u8,
                           "y = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_b, r_u8);
            }
        }
    }

    fn single_pix_correct(cymk_pix: [u8; 4], rgb_pix_true: [u8; 3]) {
        let rgb_pix = cmyk_to_rgb(&cymk_pix);
        assert_eq!(rgb_pix[0], rgb_pix_true[0]);
        assert_eq!(rgb_pix[1], rgb_pix_true[1]);
        assert_eq!(rgb_pix[2], rgb_pix_true[2]);
    }

    #[test]
    fn test_grey_colors() {
        single_pix_correct([0, 0, 0, 0], [255, 255, 255]);
        single_pix_correct([0, 0, 0, 254], [1, 1, 1]);
        single_pix_correct([0, 0, 0, 253], [2, 2, 2]);
    }

}

<|endoftext|>


<commit_before>// This defines a base target-configuration for native UEFI systems. The UEFI specification has
// quite detailed sections on the ABI of all the supported target architectures. In almost all
// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN
// documentation.
// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic
// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated
// by the loader if the pre-chosen memory location is already in use.
// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
// code runs in the same environment, no process separation is supported.

use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions};
use std::default::Default;

pub fn opts() -> TargetOptions {
    let mut pre_link_args = LinkArgs::new();

    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), vec![
            // Suppress the verbose logo and authorship debugging output, which would needlessly
            // clog any log files.
            "/NOLOGO".to_string(),

            // UEFI is fully compatible to non-executable data pages. Tell the compiler that
            // non-code sections can be marked as non-executable, including stack pages. In fact,
            // firmware might enforce this, so we better let the linker know about this, so it
            // will fail if the compiler ever tries placing code on the stack (e.g., trampoline
            // constructs and alike).
            "/NXCOMPAT".to_string(),

            // There is no runtime for UEFI targets, prevent them from being linked. UEFI targets
            // must be freestanding.
            "/nodefaultlib".to_string(),

            // Non-standard subsystems have no default entry-point in PE+ files. We have to define
            // one. "efi_main" seems to be a common choice amongst other implementations and the
            // spec.
            "/entry:efi_main".to_string(),

            // COFF images have a "Subsystem" field in their header, which defines what kind of
            // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION,
            // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION,
            // which is very likely the most common option. Individual projects can override this
            // with custom linker flags.
            // The subsystem-type only has minor effects on the application. It defines the memory
            // regions the application is loaded into (runtime-drivers need to be put into
            // reserved areas), as well as whether a return from the entry-point is treated as
            // exit (default for applications).
            "/subsystem:efi_application".to_string(),
        ]);

    TargetOptions {
        dynamic_linking: false,
        executables: true,
        disable_redzone: true,
        exe_suffix: ".efi".to_string(),
        allows_weak_linkage: false,
        panic_strategy: PanicStrategy::Abort,
        stack_probes: true,
        singlethread: true,
        emit_debug_gdb_scripts: false,

        linker: Some("lld-link".to_string()),
        lld_flavor: LldFlavor::Link,
        pre_link_args,

        .. Default::default()
    }
}
<commit_msg>Rollup merge of #58976 - phil-opp:patch-2, r=alexcrichton<commit_after>// This defines a base target-configuration for native UEFI systems. The UEFI specification has
// quite detailed sections on the ABI of all the supported target architectures. In almost all
// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN
// documentation.
// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic
// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated
// by the loader if the pre-chosen memory location is already in use.
// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
// code runs in the same environment, no process separation is supported.

use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions};
use std::default::Default;

pub fn opts() -> TargetOptions {
    let mut pre_link_args = LinkArgs::new();

    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), vec![
            // Suppress the verbose logo and authorship debugging output, which would needlessly
            // clog any log files.
            "/NOLOGO".to_string(),

            // UEFI is fully compatible to non-executable data pages. Tell the compiler that
            // non-code sections can be marked as non-executable, including stack pages. In fact,
            // firmware might enforce this, so we better let the linker know about this, so it
            // will fail if the compiler ever tries placing code on the stack (e.g., trampoline
            // constructs and alike).
            "/NXCOMPAT".to_string(),

            // There is no runtime for UEFI targets, prevent them from being linked. UEFI targets
            // must be freestanding.
            "/nodefaultlib".to_string(),

            // Non-standard subsystems have no default entry-point in PE+ files. We have to define
            // one. "efi_main" seems to be a common choice amongst other implementations and the
            // spec.
            "/entry:efi_main".to_string(),

            // COFF images have a "Subsystem" field in their header, which defines what kind of
            // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION,
            // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION,
            // which is very likely the most common option. Individual projects can override this
            // with custom linker flags.
            // The subsystem-type only has minor effects on the application. It defines the memory
            // regions the application is loaded into (runtime-drivers need to be put into
            // reserved areas), as well as whether a return from the entry-point is treated as
            // exit (default for applications).
            "/subsystem:efi_application".to_string(),
        ]);

    TargetOptions {
        dynamic_linking: false,
        executables: true,
        disable_redzone: true,
        exe_suffix: ".efi".to_string(),
        allows_weak_linkage: false,
        panic_strategy: PanicStrategy::Abort,
        stack_probes: true,
        singlethread: true,
        emit_debug_gdb_scripts: false,

        linker: Some("rust-lld".to_string()),
        lld_flavor: LldFlavor::Link,
        pre_link_args,

        .. Default::default()
    }
}
<|endoftext|>


<commit_before>use crate::{
    prelude::{verify::TxErr, *},
    serializer::*,
};
use std::io::{self, Cursor, Error};

#[derive(Clone, Debug, PartialEq)]
pub enum RequestType {
    Batch(Vec<MsgRequest>),
    Single(MsgRequest),
}

impl RequestType {
    pub fn serialize(&self, buf: &mut Vec<u8>) {
        match self {
            RequestType::Batch(batch) => {
                buf.push(0);
                buf.push_u32(batch.len() as u32);
                for req in batch {
                    req.serialize(buf);
                }
            }
            RequestType::Single(req) => {
                buf.push(1);
                req.serialize(buf);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            0 => {
                let len = cursor.take_u32()?;
                let mut batch = Vec::with_capacity(len as usize);
                for _ in 0..len {
                    batch.push(MsgRequest::deserialize(cursor)?);
                }
                Ok(RequestType::Batch(batch))
            }
            1 => Ok(RequestType::Single(MsgRequest::deserialize(cursor)?)),
            _ => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "failed to deserialize type",
                ))
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum ResponseType {
    Batch(Vec<MsgResponse>),
    Single(MsgResponse),
}

impl ResponseType {
    pub fn unwrap_batch(self) -> Vec<MsgResponse> {
        match self {
            ResponseType::Batch(batch) => batch,
            _ => panic!("expected batch response type"),
        }
    }

    pub fn unwrap_single(self) -> MsgResponse {
        match self {
            ResponseType::Single(res) => res,
            _ => panic!("expected single response type"),
        }
    }

    pub fn serialize(&self, buf: &mut Vec<u8>) {
        match self {
            ResponseType::Batch(batch) => {
                buf.push(0);
                buf.push_u32(batch.len() as u32);
                for res in batch {
                    res.serialize(buf);
                }
            }
            ResponseType::Single(res) => {
                buf.push(1);
                res.serialize(buf);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            0 => {
                let len = cursor.take_u32()?;
                let mut batch = Vec::with_capacity(len as usize);
                for _ in 0..len {
                    batch.push(MsgResponse::deserialize(cursor)?);
                }
                Ok(ResponseType::Batch(batch))
            }
            1 => Ok(ResponseType::Single(MsgResponse::deserialize(cursor)?)),
            _ => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "failed to deserialize type",
                ))
            }
        }
    }
}

#[repr(u8)]
pub enum MsgType {
    Error = 0,
    Broadcast = 10,
    GetProperties = 20,
    GetBlock = 21,
    GetAddressInfo = 22,
}

#[derive(Clone, Debug, PartialEq)]
pub enum MsgRequest {
    Broadcast(TxVariant),
    GetProperties,
    GetBlock(u64), // height
    GetAddressInfo(ScriptHash),
}

impl MsgRequest {
    pub fn serialize(&self, buf: &mut Vec<u8>) {
        match self {
            MsgRequest::Broadcast(tx) => {
                buf.reserve_exact(4096);
                buf.push(MsgType::Broadcast as u8);
                tx.serialize(buf);
            }
            MsgRequest::GetProperties => buf.push(MsgType::GetProperties as u8),
            MsgRequest::GetBlock(height) => {
                buf.reserve_exact(9);
                buf.push(MsgType::GetBlock as u8);
                buf.push_u64(*height);
            }
            MsgRequest::GetAddressInfo(addr) => {
                buf.reserve_exact(33);
                buf.push(MsgType::GetAddressInfo as u8);
                buf.push_script_hash(addr);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::Broadcast as u8 => {
                let tx = TxVariant::deserialize(cursor)
                    .ok_or_else(|| Error::new(io::ErrorKind::InvalidData, "failed to decode tx"))?;
                Ok(MsgRequest::Broadcast(tx))
            }
            t if t == MsgType::GetProperties as u8 => Ok(MsgRequest::GetProperties),
            t if t == MsgType::GetBlock as u8 => {
                let height = cursor.take_u64()?;
                Ok(MsgRequest::GetBlock(height))
            }
            t if t == MsgType::GetAddressInfo as u8 => {
                let addr = cursor.take_script_hash()?;
                Ok(MsgRequest::GetAddressInfo(addr))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg request",
            )),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ErrorKind {
    Io,
    BytesRemaining,
    InvalidHeight,
    TxValidation(TxErr),
}

impl ErrorKind {
    fn serialize(self, buf: &mut Vec<u8>) {
        match self {
            ErrorKind::Io => buf.push(0),
            ErrorKind::BytesRemaining => buf.push(1),
            ErrorKind::InvalidHeight => buf.push(2),
            ErrorKind::TxValidation(err) => {
                buf.push(3);
                err.serialize(buf);
            }
        }
    }

    fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        Ok(match tag {
            0 => ErrorKind::Io,
            1 => ErrorKind::BytesRemaining,
            2 => ErrorKind::InvalidHeight,
            3 => ErrorKind::TxValidation(TxErr::deserialize(cursor)?),
            _ => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "failed to deserialize ErrorKind",
                ))
            }
        })
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MsgResponse {
    Error(ErrorKind),
    Broadcast,
    GetProperties(Properties),
    GetBlock(SignedBlock),
    GetAddressInfo(AddressInfo),
}

impl MsgResponse {
    pub fn is_err(&self) -> bool {
        match self {
            MsgResponse::Error(..) => true,
            _ => false,
        }
    }

    pub fn serialize(&self, buf: &mut Vec<u8>) {
        use std::mem;

        match self {
            MsgResponse::Error(err) => {
                buf.reserve_exact(2048);
                buf.push(MsgType::Error as u8);
                err.serialize(buf);
            }
            MsgResponse::Broadcast => buf.push(MsgType::Broadcast as u8),
            MsgResponse::GetProperties(props) => {
                buf.reserve_exact(4096 + mem::size_of::<Properties>());
                buf.push(MsgType::GetProperties as u8);
                buf.push_u64(props.height);
                {
                    let mut tx_buf = Vec::with_capacity(4096);
                    props.owner.serialize(&mut tx_buf);
                    buf.extend_from_slice(&tx_buf);
                }
                buf.push_asset(props.network_fee);
                buf.push_asset(props.token_supply);
            }
            MsgResponse::GetBlock(block) => {
                buf.reserve_exact(1_048_576);
                buf.push(MsgType::GetBlock as u8);
                block.serialize_with_tx(buf);
            }
            MsgResponse::GetAddressInfo(info) => {
                buf.reserve_exact(1 + (mem::size_of::<Asset>() * 3));
                buf.push(MsgType::GetAddressInfo as u8);
                buf.push_asset(info.net_fee);
                buf.push_asset(info.addr_fee);
                buf.push_asset(info.balance);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::Error as u8 => {
                let err = ErrorKind::deserialize(cursor)?;
                Ok(MsgResponse::Error(err))
            }
            t if t == MsgType::Broadcast as u8 => Ok(MsgResponse::Broadcast),
            t if t == MsgType::GetProperties as u8 => {
                let height = cursor.take_u64()?;
                let owner = {
                    let tx = TxVariant::deserialize(cursor).ok_or_else(|| {
                        Error::new(io::ErrorKind::InvalidData, "failed to deserialize owner tx")
                    })?;
                    match tx {
                        TxVariant::V0(ref var) => match var {
                            TxVariantV0::OwnerTx(_) => Box::new(tx),
                            _ => {
                                return Err(Error::new(
                                    io::ErrorKind::InvalidData,
                                    "expected owner tx",
                                ))
                            }
                        },
                    }
                };
                let network_fee = cursor.take_asset()?;
                let token_supply = cursor.take_asset()?;
                Ok(MsgResponse::GetProperties(Properties {
                    height,
                    owner,
                    network_fee,
                    token_supply,
                }))
            }
            t if t == MsgType::GetBlock as u8 => {
                let block = SignedBlock::deserialize_with_tx(cursor)
                    .ok_or_else(|| Error::from(io::ErrorKind::UnexpectedEof))?;
                Ok(MsgResponse::GetBlock(block))
            }
            t if t == MsgType::GetAddressInfo as u8 => {
                let net_fee = cursor.take_asset()?;
                let addr_fee = cursor.take_asset()?;
                let balance = cursor.take_asset()?;
                Ok(MsgResponse::GetAddressInfo(AddressInfo {
                    net_fee,
                    addr_fee,
                    balance,
                }))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg response",
            )),
        }
    }
}
<commit_msg>Use hex numbers for the network msg type<commit_after>use crate::{
    prelude::{verify::TxErr, *},
    serializer::*,
};
use std::io::{self, Cursor, Error};

#[derive(Clone, Debug, PartialEq)]
pub enum RequestType {
    Batch(Vec<MsgRequest>),
    Single(MsgRequest),
}

impl RequestType {
    pub fn serialize(&self, buf: &mut Vec<u8>) {
        match self {
            RequestType::Batch(batch) => {
                buf.push(0);
                buf.push_u32(batch.len() as u32);
                for req in batch {
                    req.serialize(buf);
                }
            }
            RequestType::Single(req) => {
                buf.push(1);
                req.serialize(buf);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            0 => {
                let len = cursor.take_u32()?;
                let mut batch = Vec::with_capacity(len as usize);
                for _ in 0..len {
                    batch.push(MsgRequest::deserialize(cursor)?);
                }
                Ok(RequestType::Batch(batch))
            }
            1 => Ok(RequestType::Single(MsgRequest::deserialize(cursor)?)),
            _ => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "failed to deserialize type",
                ))
            }
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum ResponseType {
    Batch(Vec<MsgResponse>),
    Single(MsgResponse),
}

impl ResponseType {
    pub fn unwrap_batch(self) -> Vec<MsgResponse> {
        match self {
            ResponseType::Batch(batch) => batch,
            _ => panic!("expected batch response type"),
        }
    }

    pub fn unwrap_single(self) -> MsgResponse {
        match self {
            ResponseType::Single(res) => res,
            _ => panic!("expected single response type"),
        }
    }

    pub fn serialize(&self, buf: &mut Vec<u8>) {
        match self {
            ResponseType::Batch(batch) => {
                buf.push(0);
                buf.push_u32(batch.len() as u32);
                for res in batch {
                    res.serialize(buf);
                }
            }
            ResponseType::Single(res) => {
                buf.push(1);
                res.serialize(buf);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            0 => {
                let len = cursor.take_u32()?;
                let mut batch = Vec::with_capacity(len as usize);
                for _ in 0..len {
                    batch.push(MsgResponse::deserialize(cursor)?);
                }
                Ok(ResponseType::Batch(batch))
            }
            1 => Ok(ResponseType::Single(MsgResponse::deserialize(cursor)?)),
            _ => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "failed to deserialize type",
                ))
            }
        }
    }
}

#[repr(u8)]
pub enum MsgType {
    // Returned to clients when an error occurred processing a request
    Error = 0x01,

    // Operations that can update the blockchain state
    Broadcast = 0x10,

    // Getters
    GetProperties = 0x20,
    GetBlock = 0x21,
    GetAddressInfo = 0x22,
}

#[derive(Clone, Debug, PartialEq)]
pub enum MsgRequest {
    Broadcast(TxVariant),
    GetProperties,
    GetBlock(u64), // height
    GetAddressInfo(ScriptHash),
}

impl MsgRequest {
    pub fn serialize(&self, buf: &mut Vec<u8>) {
        match self {
            MsgRequest::Broadcast(tx) => {
                buf.reserve_exact(4096);
                buf.push(MsgType::Broadcast as u8);
                tx.serialize(buf);
            }
            MsgRequest::GetProperties => buf.push(MsgType::GetProperties as u8),
            MsgRequest::GetBlock(height) => {
                buf.reserve_exact(9);
                buf.push(MsgType::GetBlock as u8);
                buf.push_u64(*height);
            }
            MsgRequest::GetAddressInfo(addr) => {
                buf.reserve_exact(33);
                buf.push(MsgType::GetAddressInfo as u8);
                buf.push_script_hash(addr);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::Broadcast as u8 => {
                let tx = TxVariant::deserialize(cursor)
                    .ok_or_else(|| Error::new(io::ErrorKind::InvalidData, "failed to decode tx"))?;
                Ok(MsgRequest::Broadcast(tx))
            }
            t if t == MsgType::GetProperties as u8 => Ok(MsgRequest::GetProperties),
            t if t == MsgType::GetBlock as u8 => {
                let height = cursor.take_u64()?;
                Ok(MsgRequest::GetBlock(height))
            }
            t if t == MsgType::GetAddressInfo as u8 => {
                let addr = cursor.take_script_hash()?;
                Ok(MsgRequest::GetAddressInfo(addr))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg request",
            )),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ErrorKind {
    Io,
    BytesRemaining,
    InvalidHeight,
    TxValidation(TxErr),
}

impl ErrorKind {
    fn serialize(self, buf: &mut Vec<u8>) {
        match self {
            ErrorKind::Io => buf.push(0),
            ErrorKind::BytesRemaining => buf.push(1),
            ErrorKind::InvalidHeight => buf.push(2),
            ErrorKind::TxValidation(err) => {
                buf.push(3);
                err.serialize(buf);
            }
        }
    }

    fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        Ok(match tag {
            0 => ErrorKind::Io,
            1 => ErrorKind::BytesRemaining,
            2 => ErrorKind::InvalidHeight,
            3 => ErrorKind::TxValidation(TxErr::deserialize(cursor)?),
            _ => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "failed to deserialize ErrorKind",
                ))
            }
        })
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum MsgResponse {
    Error(ErrorKind),
    Broadcast,
    GetProperties(Properties),
    GetBlock(SignedBlock),
    GetAddressInfo(AddressInfo),
}

impl MsgResponse {
    pub fn is_err(&self) -> bool {
        match self {
            MsgResponse::Error(..) => true,
            _ => false,
        }
    }

    pub fn serialize(&self, buf: &mut Vec<u8>) {
        use std::mem;

        match self {
            MsgResponse::Error(err) => {
                buf.reserve_exact(2048);
                buf.push(MsgType::Error as u8);
                err.serialize(buf);
            }
            MsgResponse::Broadcast => buf.push(MsgType::Broadcast as u8),
            MsgResponse::GetProperties(props) => {
                buf.reserve_exact(4096 + mem::size_of::<Properties>());
                buf.push(MsgType::GetProperties as u8);
                buf.push_u64(props.height);
                {
                    let mut tx_buf = Vec::with_capacity(4096);
                    props.owner.serialize(&mut tx_buf);
                    buf.extend_from_slice(&tx_buf);
                }
                buf.push_asset(props.network_fee);
                buf.push_asset(props.token_supply);
            }
            MsgResponse::GetBlock(block) => {
                buf.reserve_exact(1_048_576);
                buf.push(MsgType::GetBlock as u8);
                block.serialize_with_tx(buf);
            }
            MsgResponse::GetAddressInfo(info) => {
                buf.reserve_exact(1 + (mem::size_of::<Asset>() * 3));
                buf.push(MsgType::GetAddressInfo as u8);
                buf.push_asset(info.net_fee);
                buf.push_asset(info.addr_fee);
                buf.push_asset(info.balance);
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::Error as u8 => {
                let err = ErrorKind::deserialize(cursor)?;
                Ok(MsgResponse::Error(err))
            }
            t if t == MsgType::Broadcast as u8 => Ok(MsgResponse::Broadcast),
            t if t == MsgType::GetProperties as u8 => {
                let height = cursor.take_u64()?;
                let owner = {
                    let tx = TxVariant::deserialize(cursor).ok_or_else(|| {
                        Error::new(io::ErrorKind::InvalidData, "failed to deserialize owner tx")
                    })?;
                    match tx {
                        TxVariant::V0(ref var) => match var {
                            TxVariantV0::OwnerTx(_) => Box::new(tx),
                            _ => {
                                return Err(Error::new(
                                    io::ErrorKind::InvalidData,
                                    "expected owner tx",
                                ))
                            }
                        },
                    }
                };
                let network_fee = cursor.take_asset()?;
                let token_supply = cursor.take_asset()?;
                Ok(MsgResponse::GetProperties(Properties {
                    height,
                    owner,
                    network_fee,
                    token_supply,
                }))
            }
            t if t == MsgType::GetBlock as u8 => {
                let block = SignedBlock::deserialize_with_tx(cursor)
                    .ok_or_else(|| Error::from(io::ErrorKind::UnexpectedEof))?;
                Ok(MsgResponse::GetBlock(block))
            }
            t if t == MsgType::GetAddressInfo as u8 => {
                let net_fee = cursor.take_asset()?;
                let addr_fee = cursor.take_asset()?;
                let balance = cursor.take_asset()?;
                Ok(MsgResponse::GetAddressInfo(AddressInfo {
                    net_fee,
                    addr_fee,
                    balance,
                }))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg response",
            )),
        }
    }
}
<|endoftext|>