<commit_before>#[macro_use]
extern crate bitflags;
pub mod error;
use error::Result;
use std::str::FromStr;

bitflags! {
    pub flags ExprType: u64 {
        const TE_VARIABLE = 0,
        const TE_CONSTANT = 1,
        const TE_FUNCTION0 = 8,
        const TE_FUNCTION1 = 9,
        const TE_FUNCTION2 = 10,
        const TE_FUNCTION3 = 11,
        const TE_FUNCTION4 = 12,
        const TE_FUNCTION5 = 13,
        const TE_FUNCTION6 = 14,
        const TE_FUNCTION7 = 15,
        const TE_CLOSURE0 = 16,
        const TE_CLOSURE1 = 17,
        const TE_CLOSURE2 = 18,
        const TE_CLOSURE3 = 19,
        const TE_CLOSURE4 = 20,
        const TE_CLOSURE5 = 21,
        const TE_CLOSURE6 = 22,
        const TE_CLOSURE7 = 23,
        const TE_FLAG_PURE = 32,
        const TOK_NULL = 24,
        const TOK_ERROR = 25,
        const TOK_END = 26,
        const TOK_SEP = 27,
        const TOK_OPEN = 28,
        const TOK_CLOSE = 29,
        const TOK_NUMBER = 30,
        const TOK_VARIABLE = 31,
        const TOK_INFIX = 32,
        const T_MASK =  0x0000001F
    }
}

macro_rules! type_mask {
    ($x:expr) => ($x & T_MASK)
}

macro_rules! is_pure {
    ($x:expr) => (($x & TE_FLAG_PURE).bits() != 0)
}

macro_rules! is_function {
    ($x:expr) => (($x & TE_FUNCTION0).bits() != 0)
}

macro_rules! is_closure {
    ($x:expr) => (($x & TE_CLOSURE0).bits() != 0)
}

macro_rules! arity {
    ($x:expr) => (if($x & (TE_FUNCTION0 | TE_CLOSURE0)).bits() != 0 { $x.bits() & 0x00000007 } else { 0 })
}

// todo: function pointers?
const FUNCTIONS: [&'static str; 21] = ["abs", "acos", "asin", "atan", "atan2", "ceil", "cos",
                                       "cosh", "e", "exp", "floor", "ln", "log", "log10",
                                       "pi", "pow", "sin", "sinh", "sqrt", "tan", "tanh" ];
const FUNCTION_TYPES: [ExprType; 21] = [ TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION2, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION0, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION0,
                                         TE_FUNCTION2, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION1];
pub struct Expr {
    pub e_type: ExprType,
    pub value: f64,
    pub bound: i8,
    pub function: i8,
    pub parameters: i8
}

impl Expr {
    fn new() -> Expr {
        Expr {
            e_type: TOK_NULL,
            value: 0.0,
            bound: 0,
            function: 0,
            parameters: 0,
        }
    }
}

pub struct Variable {
    pub name: String,
    pub address: i8,
    pub v_type: ExprType,
    pub context: i8,
}

impl Variable {
    fn new(name: &str, v_type: ExprType) -> Variable {
        Variable {
            name: String::from(name),
            address: 0,
            v_type: v_type,
            context: 0,
        }
    }
}

impl Clone for Variable {
    fn clone(&self) -> Variable {
        Variable {
            name: self.name.clone(),
            address: self.address,
            v_type: self.v_type,
            context: self.context
        }
    }
}

struct State {
    pub next: String,
    pub s_type: ExprType,
    pub n_idx: usize,
    pub value: f64,
    pub bound: i8,
    pub function: i8,
    pub context: i8,
    pub lookup: Vec<Variable>,
}

impl State {
    fn new(expression: &str) -> State {
        State {
            next: String::from(expression),
            s_type: TOK_NULL,
            n_idx: 0,
            value: 0.0,
            bound: 0,
            function: 0,
            context: 0,
            lookup: Vec::<Variable>::new()
        }
    }
}

fn find_lookup(s: &State, txt: &str) -> Option<Variable> {
    for var in &s.lookup {
        if &(*var.name) == txt {
            return Some((*var).clone());
        }
    }

    None
}

fn find_builtin(txt: &str) -> Option<Variable> {
    if let Ok(idx) = FUNCTIONS.binary_search(&txt) {
        return Some(Variable::new(txt, FUNCTION_TYPES[idx]));
    }
    None
}

fn next_token(s: &mut State) -> Result<String> {
    s.s_type = TOK_NULL;

    while s.s_type == TOK_NULL {
        if s.n_idx == s.next.len() {
            s.s_type = TOK_END;
            break;
        }

        let next_char = s.next.as_bytes()[s.n_idx] as char;
        // try reading a number
        if (next_char >= '0' && next_char <= '9') || next_char == '.' {
            let mut num_str = String::new();
            let mut c = next_char;

            // extract the number part to separate string which we then convert to f64
            while s.n_idx < s.next.len() && (c >= '0' && c <= '9') || c == '.' {
                num_str.push(c);
                s.n_idx += 1;
                if s.n_idx < s.next.len() {
                    c = s.next.as_bytes()[s.n_idx] as char;
                }
            }
            s.value  = try!(f64::from_str(&num_str));
            s.s_type = TOK_NUMBER;
        } else {
            // look for a variable or builting function call
            if next_char >= 'a' && next_char <= 'z' {
                let mut txt_str = String::new();
                let mut c = next_char;

                while s.n_idx < s.next.len() && (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
                    txt_str.push(c);
                    s.n_idx += 1;
                    if s.n_idx < s.next.len() {
                        c = s.next.as_bytes()[s.n_idx] as char;
                    }
                }

                let mut var = find_lookup(&s, &txt_str);
                if let None = var {
                   var = find_builtin(&txt_str);
                }

                if let Some(v) = var {
                    match type_mask!(s.s_type) {
                        TE_VARIABLE => { s.s_type = TOK_VARIABLE; s.bound = v.address; },
                        TE_CLOSURE0 => s.context = v.context,
                        TE_CLOSURE1 => s.context = v.context,
                        TE_CLOSURE2 => s.context = v.context,
                        TE_CLOSURE3 => s.context = v.context,
                        TE_CLOSURE4 => s.context = v.context,
                        TE_CLOSURE5 => s.context = v.context,
                        TE_CLOSURE6 => s.context = v.context,
                        TE_CLOSURE7 => s.context = v.context,
                        TE_FUNCTION0 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION1 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION2 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION3 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION4 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION5 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION6 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION7 => { s.s_type = v.v_type; s.function = v.address; },
                        _ => {}
                    }
                }
                else {
                    s.s_type = TOK_ERROR;
                }
            } else {
                // todo: set function
                // look for an operator or special character
                match s.next.as_bytes()[s.n_idx] as char {
                    '+' => s.s_type = TOK_INFIX,
                    '-' => s.s_type = TOK_INFIX,
                    '*' => s.s_type = TOK_INFIX,
                    '/' => s.s_type = TOK_INFIX,
                    '^' => s.s_type = TOK_INFIX,
                    '%' => s.s_type = TOK_INFIX,
                    '(' => s.s_type = TOK_OPEN,
                    ')' => s.s_type = TOK_CLOSE,
                    ',' => s.s_type = TOK_SEP,
                    ' ' | '\t' | '\n' |'\r' => {},
                      _ => s.s_type = TOK_ERROR
                }
                s.n_idx += 1;
            }
        }
    }

    Ok(String::new())
}

fn base(s: &mut State) -> Result<Expr> {
    Ok(Expr::new())
}

fn power(s: &mut State) -> Result<Expr> {
    let sign = 1;
    // todo: check functions here
    while s.s_type == TOK_INFIX {
        try!(next_token(s));
    }

    // todo: new_expr if sign != 1, set function
    let ret = try!(base(s));

    Ok(ret)
}

// todo: ifdef TE_POW_FROM_RIGHT
fn factor(s: &mut State) -> Result<Expr> {
    let ret = try!(power(s));

    // todo: check functions here
    while s.s_type == TOK_INFIX {
        // todo: fetch and set functions
        try!(next_token(s));
    }

    Ok(ret)
}

fn term(s: &mut State) -> Result<Expr> {
    let ret = try!(factor(s));

    // todo: check functions here
    while s.s_type == TOK_INFIX {
        // todo: fetch and set functions
        try!(next_token(s));
    }

    Ok(ret)
}

fn expr(s: &mut State) -> Result<Expr> {
    let ret = try!(term(s));

    // todo: check functions here
    while s.s_type == TOK_INFIX {
        // todo: fetch and set functions
        try!(next_token(s));
    }

    Ok(ret)
}

fn list(s: &mut State) -> Result<Expr> {
    let ret = try!(expr(s));

    while s.s_type == TOK_SEP {
        try!(next_token(s));
        // todo: new expr
        // todo: set function
    }

    Ok(ret)
}

fn optimize(n: &mut Expr) {
    // evaluates as much as possible
    if n.e_type == TE_CONSTANT { return; }
    if n.e_type == TE_VARIABLE { return; }

    if (n.e_type & TE_FLAG_PURE).bits() != 0  {
        let mut known = 1;
        let arity = arity!(n.e_type);

        for i in 0..arity {
            // todo: optimize parameters
        }

        if known != 0 {
            n.value = eval(&n);
            n.e_type = TE_CONSTANT;
        }
    }
}

pub fn compile(expression: &str, variables: Option<Vec<Variable>> ) -> Result<Option<Expr>> {
    let mut s = State::new(expression);
    if let Some(vars) = variables {
        s.lookup = vars;
    }

    arity!(s.s_type);
    try!(next_token(&mut s));
    let mut root = try!(list(&mut s));

    if s.s_type != TOK_END {
        return Ok(None)
    }

    optimize(&mut root);
    Ok(Some(root))
}

pub fn interp(expression: &str) -> Result<f64> {
    let e = try!(compile(expression, None));

    if let Some(expr) = e {
        return Ok(eval(&expr));
    }

    Err(error::TinyExprError::Other(String::from("NaN")))
}

pub fn eval(n: &Expr) -> f64 {
    0.0
}
<commit_msg>added new_expr stub<commit_after>#[macro_use]
extern crate bitflags;
pub mod error;
use error::Result;
use std::str::FromStr;

bitflags! {
    pub flags ExprType: u64 {
        const TE_VARIABLE = 0,
        const TE_CONSTANT = 1,
        const TE_FUNCTION0 = 8,
        const TE_FUNCTION1 = 9,
        const TE_FUNCTION2 = 10,
        const TE_FUNCTION3 = 11,
        const TE_FUNCTION4 = 12,
        const TE_FUNCTION5 = 13,
        const TE_FUNCTION6 = 14,
        const TE_FUNCTION7 = 15,
        const TE_CLOSURE0 = 16,
        const TE_CLOSURE1 = 17,
        const TE_CLOSURE2 = 18,
        const TE_CLOSURE3 = 19,
        const TE_CLOSURE4 = 20,
        const TE_CLOSURE5 = 21,
        const TE_CLOSURE6 = 22,
        const TE_CLOSURE7 = 23,
        const TE_FLAG_PURE = 32,
        const TOK_NULL = 24,
        const TOK_ERROR = 25,
        const TOK_END = 26,
        const TOK_SEP = 27,
        const TOK_OPEN = 28,
        const TOK_CLOSE = 29,
        const TOK_NUMBER = 30,
        const TOK_VARIABLE = 31,
        const TOK_INFIX = 32,
        const T_MASK =  0x0000001F
    }
}

macro_rules! type_mask {
    ($x:expr) => ($x & T_MASK)
}

macro_rules! is_pure {
    ($x:expr) => (($x & TE_FLAG_PURE).bits() != 0)
}

macro_rules! is_function {
    ($x:expr) => (($x & TE_FUNCTION0).bits() != 0)
}

macro_rules! is_closure {
    ($x:expr) => (($x & TE_CLOSURE0).bits() != 0)
}

macro_rules! arity {
    ($x:expr) => (if($x & (TE_FUNCTION0 | TE_CLOSURE0)).bits() != 0 { $x.bits() & 0x00000007 } else { 0 })
}

// todo: function pointers?
const FUNCTIONS: [&'static str; 21] = ["abs", "acos", "asin", "atan", "atan2", "ceil", "cos",
                                       "cosh", "e", "exp", "floor", "ln", "log", "log10",
                                       "pi", "pow", "sin", "sinh", "sqrt", "tan", "tanh" ];
const FUNCTION_TYPES: [ExprType; 21] = [ TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION2, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION0, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION0,
                                         TE_FUNCTION2, TE_FUNCTION1, TE_FUNCTION1, TE_FUNCTION1,
                                         TE_FUNCTION1];
pub struct Expr {
    pub e_type: ExprType,
    pub value: f64,
    pub bound: i8,
    pub function: i8,
    pub parameters: i8
}

impl Expr {
    fn new() -> Expr {
        Expr {
            e_type: TOK_NULL,
            value: 0.0,
            bound: 0,
            function: 0,
            parameters: 0,
        }
    }
}

pub struct Variable {
    pub name: String,
    pub address: i8,
    pub v_type: ExprType,
    pub context: i8,
}

impl Variable {
    fn new(name: &str, v_type: ExprType) -> Variable {
        Variable {
            name: String::from(name),
            address: 0,
            v_type: v_type,
            context: 0,
        }
    }
}

impl Clone for Variable {
    fn clone(&self) -> Variable {
        Variable {
            name: self.name.clone(),
            address: self.address,
            v_type: self.v_type,
            context: self.context
        }
    }
}

struct State {
    pub next: String,
    pub s_type: ExprType,
    pub n_idx: usize,
    pub value: f64,
    pub bound: i8,
    pub function: i8,
    pub context: i8,
    pub lookup: Vec<Variable>,
}

impl State {
    fn new(expression: &str) -> State {
        State {
            next: String::from(expression),
            s_type: TOK_NULL,
            n_idx: 0,
            value: 0.0,
            bound: 0,
            function: 0,
            context: 0,
            lookup: Vec::<Variable>::new()
        }
    }
}

// todo
fn new_expr(e_type: &ExprType) -> Expr {
    let arity = arity!(*e_type);

    // just create a new expression with new type based on old expression, no weird memcpy mumbo jumbo
    Expr::new()
}

fn find_lookup(s: &State, txt: &str) -> Option<Variable> {
    for var in &s.lookup {
        if &(*var.name) == txt {
            return Some((*var).clone());
        }
    }

    None
}

fn find_builtin(txt: &str) -> Option<Variable> {
    if let Ok(idx) = FUNCTIONS.binary_search(&txt) {
        return Some(Variable::new(txt, FUNCTION_TYPES[idx]));
    }
    None
}

fn next_token(s: &mut State) -> Result<String> {
    s.s_type = TOK_NULL;

    while s.s_type == TOK_NULL {
        if s.n_idx == s.next.len() {
            s.s_type = TOK_END;
            break;
        }

        let next_char = s.next.as_bytes()[s.n_idx] as char;
        // try reading a number
        if (next_char >= '0' && next_char <= '9') || next_char == '.' {
            let mut num_str = String::new();
            let mut c = next_char;

            // extract the number part to separate string which we then convert to f64
            while s.n_idx < s.next.len() && (c >= '0' && c <= '9') || c == '.' {
                num_str.push(c);
                s.n_idx += 1;
                if s.n_idx < s.next.len() {
                    c = s.next.as_bytes()[s.n_idx] as char;
                }
            }
            s.value  = try!(f64::from_str(&num_str));
            s.s_type = TOK_NUMBER;
        } else {
            // look for a variable or builting function call
            if next_char >= 'a' && next_char <= 'z' {
                let mut txt_str = String::new();
                let mut c = next_char;

                while s.n_idx < s.next.len() && (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
                    txt_str.push(c);
                    s.n_idx += 1;
                    if s.n_idx < s.next.len() {
                        c = s.next.as_bytes()[s.n_idx] as char;
                    }
                }

                let mut var = find_lookup(&s, &txt_str);
                if let None = var {
                   var = find_builtin(&txt_str);
                }

                if let Some(v) = var {
                    match type_mask!(s.s_type) {
                        TE_VARIABLE => { s.s_type = TOK_VARIABLE; s.bound = v.address; },
                        TE_CLOSURE0 => s.context = v.context,
                        TE_CLOSURE1 => s.context = v.context,
                        TE_CLOSURE2 => s.context = v.context,
                        TE_CLOSURE3 => s.context = v.context,
                        TE_CLOSURE4 => s.context = v.context,
                        TE_CLOSURE5 => s.context = v.context,
                        TE_CLOSURE6 => s.context = v.context,
                        TE_CLOSURE7 => s.context = v.context,
                        TE_FUNCTION0 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION1 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION2 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION3 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION4 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION5 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION6 => { s.s_type = v.v_type; s.function = v.address; },
                        TE_FUNCTION7 => { s.s_type = v.v_type; s.function = v.address; },
                        _ => {}
                    }
                }
                else {
                    s.s_type = TOK_ERROR;
                }
            } else {
                // todo: set function
                // look for an operator or special character
                match s.next.as_bytes()[s.n_idx] as char {
                    '+' => s.s_type = TOK_INFIX,
                    '-' => s.s_type = TOK_INFIX,
                    '*' => s.s_type = TOK_INFIX,
                    '/' => s.s_type = TOK_INFIX,
                    '^' => s.s_type = TOK_INFIX,
                    '%' => s.s_type = TOK_INFIX,
                    '(' => s.s_type = TOK_OPEN,
                    ')' => s.s_type = TOK_CLOSE,
                    ',' => s.s_type = TOK_SEP,
                    ' ' | '\t' | '\n' |'\r' => {},
                      _ => s.s_type = TOK_ERROR
                }
                s.n_idx += 1;
            }
        }
    }

    Ok(String::new())
}

fn base(s: &mut State) -> Result<Expr> {
    Ok(Expr::new())
}

fn power(s: &mut State) -> Result<Expr> {
    let sign = 1;
    // todo: check functions here
    while s.s_type == TOK_INFIX {
        try!(next_token(s));
    }

    // todo: new_expr if sign != 1, set function
    let ret = try!(base(s));

    Ok(ret)
}

// todo: ifdef TE_POW_FROM_RIGHT
fn factor(s: &mut State) -> Result<Expr> {
    let ret = try!(power(s));

    // todo: check functions here
    while s.s_type == TOK_INFIX {
        // todo: fetch and set functions
        try!(next_token(s));
    }

    Ok(ret)
}

fn term(s: &mut State) -> Result<Expr> {
    let ret = try!(factor(s));

    // todo: check functions here
    while s.s_type == TOK_INFIX {
        // todo: fetch and set functions
        try!(next_token(s));
    }

    Ok(ret)
}

fn expr(s: &mut State) -> Result<Expr> {
    let ret = try!(term(s));

    // todo: check functions here
    while s.s_type == TOK_INFIX {
        // todo: fetch and set functions
        try!(next_token(s));
    }

    Ok(ret)
}

fn list(s: &mut State) -> Result<Expr> {
    let ret = try!(expr(s));

    while s.s_type == TOK_SEP {
        try!(next_token(s));
        // todo: new expr
        // todo: set function
    }

    Ok(ret)
}

fn optimize(n: &mut Expr) {
    // evaluates as much as possible
    if n.e_type == TE_CONSTANT { return; }
    if n.e_type == TE_VARIABLE { return; }

    if (n.e_type & TE_FLAG_PURE).bits() != 0  {
        let mut known = 1;
        let arity = arity!(n.e_type);

        for i in 0..arity {
            // todo: optimize parameters
        }

        if known != 0 {
            n.value = eval(&n);
            n.e_type = TE_CONSTANT;
        }
    }
}

pub fn compile(expression: &str, variables: Option<Vec<Variable>> ) -> Result<Option<Expr>> {
    let mut s = State::new(expression);
    if let Some(vars) = variables {
        s.lookup = vars;
    }

    arity!(s.s_type);
    try!(next_token(&mut s));
    let mut root = try!(list(&mut s));

    if s.s_type != TOK_END {
        return Ok(None)
    }

    optimize(&mut root);
    Ok(Some(root))
}

pub fn interp(expression: &str) -> Result<f64> {
    let e = try!(compile(expression, None));

    if let Some(expr) = e {
        return Ok(eval(&expr));
    }

    Err(error::TinyExprError::Other(String::from("NaN")))
}

// todo
pub fn eval(n: &Expr) -> f64 {
    0.0
}
<|endoftext|>


<commit_before>//! [Codespawn](https://github.com/kondrak/codespawn) is a basic C++ and Rust code generator.
//! Desired API can be defined using either JSON or XML and the crate supports both reading
//! from a file or a string.
//!
//! As of 0.1 release, it's possible to generate enums, structs, functions and variables with all
//! applicable attributes and properties.
mod xml_parser;
mod json_parser;
mod string_gen;

pub mod raw_code;
pub mod fmt_code;

use std::io;
use raw_code::{RawCode};

/// Reads XML data from file and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_xml("examples/sample.xml").unwrap();
/// ```
pub fn from_xml(filename: &str) -> io::Result<RawCode> {
    xml_parser::process_xml_file(filename)
}

/// Reads XML data from a `&str` and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_xml_str("<enum name=\"Foo\"><var name=\"EnumVal1\" type=\"int\" /></enum>").unwrap();
/// ```
pub fn from_xml_str(xml: &str) -> io::Result<RawCode> {
    xml_parser::process_xml_str(xml)
}

/// Reads JSON data from file and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_json("examples/sample.json").unwrap();
/// ```
pub fn from_json(filename: &str) -> io::Result<RawCode> {
    json_parser::process_json_file(filename)
}

/// Reads JSON data from a `&str` and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_json_str("{\"enum\": { \"name\": \"Foo\",\"var\": {\"name\": \"EnumVal1\",\"type\": \"int\" }}}").unwrap();
/// ```
pub fn from_json_str(json: &str) -> io::Result<RawCode> {
    json_parser::process_json_str(json)
}
<commit_msg>added example to main docs page<commit_after>//! [Codespawn](https://github.com/kondrak/codespawn) is a basic C++ and Rust code generator.
//! Desired API can be defined using either JSON or XML and the crate supports both reading
//! from a file or a string.
//!
//! As of 0.1 release, it's possible to generate enums, structs, functions and variables with all
//! applicable attributes and properties.
//!
//!# Quick Start
//!
//!```
//!extern crate codespawn;
//!
//!fn main()
//!{
//!    // generate from XML definition
//!    let raw_code = codespawn::from_xml("examples/sample.xml").unwrap();
//!    // generate from JSON definition
//!    //let raw_code = codespawn::from_json("examples/sample.json").unwrap();
//!
//!    // generate code, store as String
//!    let cpp_code  = raw_code.to_cpp().to_string();
//!    let rust_code = raw_code.to_rust().to_string();
//!
//!    // generate and save directly to file
//!    raw_code.to_cpp().to_file("examples/sample.cpp");
//!    raw_code.to_rust().to_file("examples/sample.rs");
//!}
//!```
mod xml_parser;
mod json_parser;
mod string_gen;

pub mod raw_code;
pub mod fmt_code;

use std::io;
use raw_code::{RawCode};

/// Reads XML data from file and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_xml("examples/sample.xml").unwrap();
/// ```
pub fn from_xml(filename: &str) -> io::Result<RawCode> {
    xml_parser::process_xml_file(filename)
}

/// Reads XML data from a `&str` and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_xml_str("<enum name=\"Foo\"><var name=\"EnumVal1\" type=\"int\" /></enum>").unwrap();
/// ```
pub fn from_xml_str(xml: &str) -> io::Result<RawCode> {
    xml_parser::process_xml_str(xml)
}

/// Reads JSON data from file and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_json("examples/sample.json").unwrap();
/// ```
pub fn from_json(filename: &str) -> io::Result<RawCode> {
    json_parser::process_json_file(filename)
}

/// Reads JSON data from a `&str` and compiles it into `RawCode`
///
/// # Examples
///
/// ```
/// extern crate codespawn;
///
/// let raw_code = codespawn::from_json_str("{\"enum\": { \"name\": \"Foo\",\"var\": {\"name\": \"EnumVal1\",\"type\": \"int\" }}}").unwrap();
/// ```
pub fn from_json_str(json: &str) -> io::Result<RawCode> {
    json_parser::process_json_str(json)
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! A Gfx texture representation that works nicely with Piston libraries.

extern crate gfx;
extern crate gfx_core;
extern crate texture;
extern crate image;

pub use texture::*;

use std::path::Path;
use image::{
    DynamicImage,
    RgbaImage,
};
use gfx::CombinedError;
use gfx::format::{Srgba8, R8_G8_B8_A8};

/// Flip settings.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Flip {
    /// Does not flip.
    None,
    /// Flips image vertically.
    Vertical,
    /// Flips image horizontally.
    Horizontal,
    /// Flips image both vertically and horizontally.
    Both,
}

/// Represents a texture.
pub struct Texture<R> where R: gfx::Resources {
    /// Pixel storage for texture.
    pub surface: gfx::handle::Texture<R, R8_G8_B8_A8>,
    /// Sampler for texture.
    pub sampler: gfx::handle::Sampler<R>,
    /// View used by shader.
    pub view: gfx::handle::ShaderResourceView<R, [f32; 4]>
}

impl<R: gfx::Resources> Texture<R> {
    /// Returns empty texture.
    pub fn empty<F>(factory: &mut F) -> Result<Self, CombinedError>
        where F: gfx::Factory<R>
    {
        CreateTexture::create(factory, Format::Rgba8, &[0u8; 4], [1, 1],
                              &TextureSettings::new())
    }

    /// Creates a texture from path.
    pub fn from_path<F, P>(
        factory: &mut F,
        path: P,
        flip: Flip,
        settings: &TextureSettings,
    ) -> Result<Self, String>
        where F: gfx::Factory<R>,
              P: AsRef<Path>
    {
        let img = try!(image::open(path).map_err(|e| e.to_string()));

        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            img => img.to_rgba()
        };

        let img = match flip {
            Flip::Vertical => image::imageops::flip_vertical(&img),
            Flip::Horizontal => image::imageops::flip_horizontal(&img),
            Flip::Both => {
                let img = image::imageops::flip_vertical(&img);
                image::imageops::flip_horizontal(&img)
            }
            Flip::None => img,
        };

        Texture::from_image(factory, &img, settings).map_err(
            |e| format!("{:?}", e))
    }

    /// Creates a texture from image.
    pub fn from_image<F>(
        factory: &mut F,
        img: &RgbaImage,
        settings: &TextureSettings
    ) -> Result<Self, CombinedError>
        where F: gfx::Factory<R>
    {
        let (width, height) = img.dimensions();
        CreateTexture::create(factory, Format::Rgba8,
                              img, [width, height], settings)
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<F>(
        factory: &mut F,
        buffer: &[u8],
        width: u32,
        height: u32,
        settings: &TextureSettings
    ) -> Result<Self, CombinedError>
        where F: gfx::Factory<R>
    {
        if width == 0 || height == 0 {
            return Texture::empty(factory);
        }

        let size = [width, height];
        let buffer = texture::ops::alpha_to_rgba8(buffer, size);
        CreateTexture::create(factory, Format::Rgba8, &buffer, size, settings)
    }

    /// Updates the texture with an image.
    pub fn update<C>(
        &mut self,
        encoder: &mut gfx::Encoder<R, C>,
        img: &RgbaImage
    ) -> Result<(), gfx::UpdateError<[u16; 3]>>
        where C: gfx::CommandBuffer<R>
    {
        let (width, height) = img.dimensions();
        let offset = [0, 0];
        let size = [width, height];
        UpdateTexture::update(self, encoder, Format::Rgba8, img, offset, size)
    }
}

impl<F, R> CreateTexture<F> for Texture<R>
    where F: gfx::Factory<R>,
          R: gfx::Resources
{
    type Error = CombinedError;

    fn create<S: Into<[u32; 2]>>(
        factory: &mut F,
        _format: Format,
        memory: &[u8],
        size: S,
        settings: &TextureSettings
    ) -> Result<Self, Self::Error> {
        // Modified `Factory::create_texture_immutable_u8` for dynamic texture.
        fn create_texture<T, F, R>(
            factory: &mut F,
            kind: gfx::texture::Kind,
            data: &[&[u8]]
        ) -> Result<(
            gfx::handle::Texture<R, T::Surface>,
            gfx::handle::ShaderResourceView<R, T::View>
        ), CombinedError>
            where F: gfx::Factory<R>,
                  R: gfx::Resources,
                  T: gfx::format::TextureFormat
        {
            use gfx::{format, texture};
            use gfx::memory::{Usage, SHADER_RESOURCE};
            use gfx_core::memory::Typed;

            let surface = <T::Surface as format::SurfaceTyped>::get_surface_type();
            let num_slices = kind.get_num_slices().unwrap_or(1) as usize;
            let num_faces = if kind.is_cube() {6} else {1};
            let desc = texture::Info {
                kind: kind,
                levels: (data.len() / (num_slices * num_faces)) as texture::Level,
                format: surface,
                bind: SHADER_RESOURCE,
                usage: Usage::Dynamic,
            };
            let cty = <T::Channel as format::ChannelTyped>::get_channel_type();
            let raw = try!(factory.create_texture_raw(desc, Some(cty), Some(data)));
            let levels = (0, raw.get_info().levels - 1);
            let tex = Typed::new(raw);
            let view = try!(factory.view_texture_as_shader_resource::<T>(
                &tex, levels, format::Swizzle::new()
            ));
            Ok((tex, view))
        }

        let size = size.into();
        let (width, height) = (size[0] as u16, size[1] as u16);
        let tex_kind = gfx::texture::Kind::D2(width, height,
            gfx::texture::AaMode::Single);

        // FIXME Use get_min too. gfx has only one filter setting for both.
        let filter_method = match settings.get_mag() {
            texture::Filter::Nearest => gfx::texture::FilterMethod::Scale,
            texture::Filter::Linear => gfx::texture::FilterMethod::Bilinear,
        };
        let sampler_info = gfx::texture::SamplerInfo::new(
            filter_method,
            gfx::texture::WrapMode::Clamp
        );

        let (surface, view) = try!(create_texture::<Srgba8, F, R>(
            factory, tex_kind, &[memory])
        );
        let sampler = factory.create_sampler(sampler_info);
        Ok(Texture { surface: surface, sampler: sampler, view: view })
    }
}

impl<R, C> UpdateTexture<gfx::Encoder<R, C>> for Texture<R>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>
{
    type Error = gfx::UpdateError<[u16; 3]>;

    fn update<O, S>(
        &mut self,
        encoder: &mut gfx::Encoder<R, C>,
        format: Format,
        memory: &[u8],
        offset: O,
        size: S,
    ) -> Result<(), Self::Error>
        where O: Into<[u32; 2]>,
              S: Into<[u32; 2]>,
    {
        let offset = offset.into();
        let size = size.into();
        let tex = &self.surface;
        let face = None;
        let img_info = gfx::texture::ImageInfoCommon {
            xoffset: offset[0] as u16,
            yoffset: offset[1] as u16,
            zoffset: 0,
            width: size[0] as u16,
            height: size[1] as u16,
            depth: 0,
            format: (),
            mipmap: 0,
        };
        let data = gfx::memory::cast_slice(memory);

        match format {
            Format::Rgba8 => {
                use gfx::format::Rgba8;
                encoder.update_texture::<_, Rgba8>(tex, face, img_info, data).map_err(Into::into)
            },
        }
    }
}

impl<R> ImageSize for Texture<R> where R: gfx::Resources {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let (w, h, _, _) = self.surface.get_info().kind.get_dimensions();
        (w as u32, h as u32)
    }
}
<commit_msg>Add `#[derive(Clone, Debug, PartialEq)]` to `Texture`<commit_after>#![deny(missing_docs)]

//! A Gfx texture representation that works nicely with Piston libraries.

extern crate gfx;
extern crate gfx_core;
extern crate texture;
extern crate image;

pub use texture::*;

use std::path::Path;
use image::{
    DynamicImage,
    RgbaImage,
};
use gfx::CombinedError;
use gfx::format::{Srgba8, R8_G8_B8_A8};

/// Flip settings.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Flip {
    /// Does not flip.
    None,
    /// Flips image vertically.
    Vertical,
    /// Flips image horizontally.
    Horizontal,
    /// Flips image both vertically and horizontally.
    Both,
}

/// Represents a texture.
#[derive(Clone, Debug, PartialEq)]
pub struct Texture<R> where R: gfx::Resources {
    /// Pixel storage for texture.
    pub surface: gfx::handle::Texture<R, R8_G8_B8_A8>,
    /// Sampler for texture.
    pub sampler: gfx::handle::Sampler<R>,
    /// View used by shader.
    pub view: gfx::handle::ShaderResourceView<R, [f32; 4]>
}

impl<R: gfx::Resources> Texture<R> {
    /// Returns empty texture.
    pub fn empty<F>(factory: &mut F) -> Result<Self, CombinedError>
        where F: gfx::Factory<R>
    {
        CreateTexture::create(factory, Format::Rgba8, &[0u8; 4], [1, 1],
                              &TextureSettings::new())
    }

    /// Creates a texture from path.
    pub fn from_path<F, P>(
        factory: &mut F,
        path: P,
        flip: Flip,
        settings: &TextureSettings,
    ) -> Result<Self, String>
        where F: gfx::Factory<R>,
              P: AsRef<Path>
    {
        let img = try!(image::open(path).map_err(|e| e.to_string()));

        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            img => img.to_rgba()
        };

        let img = match flip {
            Flip::Vertical => image::imageops::flip_vertical(&img),
            Flip::Horizontal => image::imageops::flip_horizontal(&img),
            Flip::Both => {
                let img = image::imageops::flip_vertical(&img);
                image::imageops::flip_horizontal(&img)
            }
            Flip::None => img,
        };

        Texture::from_image(factory, &img, settings).map_err(
            |e| format!("{:?}", e))
    }

    /// Creates a texture from image.
    pub fn from_image<F>(
        factory: &mut F,
        img: &RgbaImage,
        settings: &TextureSettings
    ) -> Result<Self, CombinedError>
        where F: gfx::Factory<R>
    {
        let (width, height) = img.dimensions();
        CreateTexture::create(factory, Format::Rgba8,
                              img, [width, height], settings)
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<F>(
        factory: &mut F,
        buffer: &[u8],
        width: u32,
        height: u32,
        settings: &TextureSettings
    ) -> Result<Self, CombinedError>
        where F: gfx::Factory<R>
    {
        if width == 0 || height == 0 {
            return Texture::empty(factory);
        }

        let size = [width, height];
        let buffer = texture::ops::alpha_to_rgba8(buffer, size);
        CreateTexture::create(factory, Format::Rgba8, &buffer, size, settings)
    }

    /// Updates the texture with an image.
    pub fn update<C>(
        &mut self,
        encoder: &mut gfx::Encoder<R, C>,
        img: &RgbaImage
    ) -> Result<(), gfx::UpdateError<[u16; 3]>>
        where C: gfx::CommandBuffer<R>
    {
        let (width, height) = img.dimensions();
        let offset = [0, 0];
        let size = [width, height];
        UpdateTexture::update(self, encoder, Format::Rgba8, img, offset, size)
    }
}

impl<F, R> CreateTexture<F> for Texture<R>
    where F: gfx::Factory<R>,
          R: gfx::Resources
{
    type Error = CombinedError;

    fn create<S: Into<[u32; 2]>>(
        factory: &mut F,
        _format: Format,
        memory: &[u8],
        size: S,
        settings: &TextureSettings
    ) -> Result<Self, Self::Error> {
        // Modified `Factory::create_texture_immutable_u8` for dynamic texture.
        fn create_texture<T, F, R>(
            factory: &mut F,
            kind: gfx::texture::Kind,
            data: &[&[u8]]
        ) -> Result<(
            gfx::handle::Texture<R, T::Surface>,
            gfx::handle::ShaderResourceView<R, T::View>
        ), CombinedError>
            where F: gfx::Factory<R>,
                  R: gfx::Resources,
                  T: gfx::format::TextureFormat
        {
            use gfx::{format, texture};
            use gfx::memory::{Usage, SHADER_RESOURCE};
            use gfx_core::memory::Typed;

            let surface = <T::Surface as format::SurfaceTyped>::get_surface_type();
            let num_slices = kind.get_num_slices().unwrap_or(1) as usize;
            let num_faces = if kind.is_cube() {6} else {1};
            let desc = texture::Info {
                kind: kind,
                levels: (data.len() / (num_slices * num_faces)) as texture::Level,
                format: surface,
                bind: SHADER_RESOURCE,
                usage: Usage::Dynamic,
            };
            let cty = <T::Channel as format::ChannelTyped>::get_channel_type();
            let raw = try!(factory.create_texture_raw(desc, Some(cty), Some(data)));
            let levels = (0, raw.get_info().levels - 1);
            let tex = Typed::new(raw);
            let view = try!(factory.view_texture_as_shader_resource::<T>(
                &tex, levels, format::Swizzle::new()
            ));
            Ok((tex, view))
        }

        let size = size.into();
        let (width, height) = (size[0] as u16, size[1] as u16);
        let tex_kind = gfx::texture::Kind::D2(width, height,
            gfx::texture::AaMode::Single);

        // FIXME Use get_min too. gfx has only one filter setting for both.
        let filter_method = match settings.get_mag() {
            texture::Filter::Nearest => gfx::texture::FilterMethod::Scale,
            texture::Filter::Linear => gfx::texture::FilterMethod::Bilinear,
        };
        let sampler_info = gfx::texture::SamplerInfo::new(
            filter_method,
            gfx::texture::WrapMode::Clamp
        );

        let (surface, view) = try!(create_texture::<Srgba8, F, R>(
            factory, tex_kind, &[memory])
        );
        let sampler = factory.create_sampler(sampler_info);
        Ok(Texture { surface: surface, sampler: sampler, view: view })
    }
}

impl<R, C> UpdateTexture<gfx::Encoder<R, C>> for Texture<R>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>
{
    type Error = gfx::UpdateError<[u16; 3]>;

    fn update<O, S>(
        &mut self,
        encoder: &mut gfx::Encoder<R, C>,
        format: Format,
        memory: &[u8],
        offset: O,
        size: S,
    ) -> Result<(), Self::Error>
        where O: Into<[u32; 2]>,
              S: Into<[u32; 2]>,
    {
        let offset = offset.into();
        let size = size.into();
        let tex = &self.surface;
        let face = None;
        let img_info = gfx::texture::ImageInfoCommon {
            xoffset: offset[0] as u16,
            yoffset: offset[1] as u16,
            zoffset: 0,
            width: size[0] as u16,
            height: size[1] as u16,
            depth: 0,
            format: (),
            mipmap: 0,
        };
        let data = gfx::memory::cast_slice(memory);

        match format {
            Format::Rgba8 => {
                use gfx::format::Rgba8;
                encoder.update_texture::<_, Rgba8>(tex, face, img_info, data).map_err(Into::into)
            },
        }
    }
}

impl<R> ImageSize for Texture<R> where R: gfx::Resources {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let (w, h, _, _) = self.surface.get_info().kind.get_dimensions();
        (w as u32, h as u32)
    }
}
<|endoftext|>


<commit_before>//! Reads and writes point cloud data stored in the ASPRS las file format.

#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts,
        trivial_numeric_casts, unsafe_code, unstable_features, unused_extern_crates,
        unused_import_braces, unused_qualifications)]

extern crate byteorder;

pub mod header;
pub mod file;
pub mod point;
mod io;
mod scale;
pub mod stream;
pub mod vlr;

pub use file::File;
pub use point::Point;

/// Crate-specific errors.
#[derive(Debug)]
pub enum LasError {
    /// Wraps `byteorder::Error`.
    Byteorder(byteorder::Error),
    /// Invalid classification value.
    InvalidClassification(u8),
    /// Point number of returns was out of bounds.
    InvalidNumberOfReturns(u8),
    /// Unrecognized point data format.
    InvalidPointDataFormat(u8),
    /// Point return number was out of allowed bounds.
    InvalidReturnNumber(u8),
    /// Wraps `std::io::Error`.
    Io(std::io::Error),
    /// Tried to write a point to a format it doesn't support.
    PointFormat(header::PointDataFormat, String),
}

impl std::error::Error for LasError {
    fn description(&self) -> &str {
        match *self {
            LasError::Byteorder(ref err) => err.description(),
            LasError::InvalidClassification(_) => "invalid classification",
            LasError::InvalidNumberOfReturns(_) => "invalid number of returns",
            LasError::InvalidPointDataFormat(_) => "invalid point data format",
            LasError::InvalidReturnNumber(_) => "invalid return number",
            LasError::Io(ref err) => err.description(),
            LasError::PointFormat(_, _) => "point format mismatch",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            LasError::Byteorder(ref err) => Some(err),
            LasError::Io(ref err) => Some(err),
            _ => None,
        }
    }
}

impl std::fmt::Display for LasError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            LasError::Byteorder(ref err) => write!(f, "Byteorder error: {}", err),
            LasError::InvalidClassification(n) => write!(f, "Invalid classification: {}", n),
            LasError::InvalidNumberOfReturns(n) => write!(f, "Invalid number of returns: {}", n),
            LasError::InvalidPointDataFormat(n) => write!(f, "Invalid point data format: {}", n),
            LasError::InvalidReturnNumber(n) => write!(f, "Invalid return number: {}", n),
            LasError::Io(ref err) => write!(f, "IO error: {}", err),
            LasError::PointFormat(format, ref field) => write!(f, "Point format mismatch for format '{}' and field '{}'", format, field),
        }
    }
}

impl From<byteorder::Error> for LasError {
    fn from(err: byteorder::Error) -> LasError {
        LasError::Byteorder(err)
    }
}

impl From<std::io::Error> for LasError {
    fn from(err: std::io::Error) -> LasError {
        LasError::Io(err)
    }
}

/// Crate-specific resuls.
pub type Result<T> = std::result::Result<T, LasError>;
<commit_msg>Re-export stream<commit_after>//! Reads and writes point cloud data stored in the ASPRS las file format.

#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts,
        trivial_numeric_casts, unsafe_code, unstable_features, unused_extern_crates,
        unused_import_braces, unused_qualifications)]

extern crate byteorder;

pub mod header;
pub mod file;
pub mod point;
mod io;
mod scale;
pub mod stream;
pub mod vlr;

pub use file::File;
pub use point::Point;
pub use stream::Stream;

/// Crate-specific errors.
#[derive(Debug)]
pub enum LasError {
    /// Wraps `byteorder::Error`.
    Byteorder(byteorder::Error),
    /// Invalid classification value.
    InvalidClassification(u8),
    /// Point number of returns was out of bounds.
    InvalidNumberOfReturns(u8),
    /// Unrecognized point data format.
    InvalidPointDataFormat(u8),
    /// Point return number was out of allowed bounds.
    InvalidReturnNumber(u8),
    /// Wraps `std::io::Error`.
    Io(std::io::Error),
    /// Tried to write a point to a format it doesn't support.
    PointFormat(header::PointDataFormat, String),
}

impl std::error::Error for LasError {
    fn description(&self) -> &str {
        match *self {
            LasError::Byteorder(ref err) => err.description(),
            LasError::InvalidClassification(_) => "invalid classification",
            LasError::InvalidNumberOfReturns(_) => "invalid number of returns",
            LasError::InvalidPointDataFormat(_) => "invalid point data format",
            LasError::InvalidReturnNumber(_) => "invalid return number",
            LasError::Io(ref err) => err.description(),
            LasError::PointFormat(_, _) => "point format mismatch",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            LasError::Byteorder(ref err) => Some(err),
            LasError::Io(ref err) => Some(err),
            _ => None,
        }
    }
}

impl std::fmt::Display for LasError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            LasError::Byteorder(ref err) => write!(f, "Byteorder error: {}", err),
            LasError::InvalidClassification(n) => write!(f, "Invalid classification: {}", n),
            LasError::InvalidNumberOfReturns(n) => write!(f, "Invalid number of returns: {}", n),
            LasError::InvalidPointDataFormat(n) => write!(f, "Invalid point data format: {}", n),
            LasError::InvalidReturnNumber(n) => write!(f, "Invalid return number: {}", n),
            LasError::Io(ref err) => write!(f, "IO error: {}", err),
            LasError::PointFormat(format, ref field) => write!(f, "Point format mismatch for format '{}' and field '{}'", format, field),
        }
    }
}

impl From<byteorder::Error> for LasError {
    fn from(err: byteorder::Error) -> LasError {
        LasError::Byteorder(err)
    }
}

impl From<std::io::Error> for LasError {
    fn from(err: std::io::Error) -> LasError {
        LasError::Io(err)
    }
}

/// Crate-specific resuls.
pub type Result<T> = std::result::Result<T, LasError>;
<|endoftext|>


<commit_before>#![feature(plugin)]
#![feature(box_syntax)]
#![feature(box_patterns)]
#![feature(iter_arith)]
#![plugin(peg_syntax_ext)]

extern crate typed_arena;
extern crate bit_set;
extern crate vec_map;
extern crate ref_slice;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;

mod session;
mod process;
mod data;
mod language;
mod connection;
mod test_runner;

pub use session::Session;
pub use language::{ ModuleLoader, Module, Test, Item };
pub use process::{ Process, ProcessStack, PrimitiveDef };
pub use connection::Connection;
pub use data::{ Value, Type, DataMode, Shape, ShapeVariant, ShapeData };
pub use test_runner::run as run_test;

mod file_io;
mod dumpfile;

pub fn add_primitives(loader: &ModuleLoader) {
    loader.add_primitive_def("file", &file_io::FILE_DEF);
    loader.add_primitive_def("dumpfile", &dumpfile::DUMPFILE_DEF);
    loader.add_primitive_fn("int", &language::FNINT);
    loader.add_primitive_fn("signed", &language::FNSIGNED);
    loader.add_primitive_fn("chunks", &language::FNCHUNKS);
}
<commit_msg>rust: iter_arith was stabilized<commit_after>#![feature(plugin)]
#![feature(box_syntax)]
#![feature(box_patterns)]
#![plugin(peg_syntax_ext)]

extern crate typed_arena;
extern crate bit_set;
extern crate vec_map;
extern crate ref_slice;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;

mod session;
mod process;
mod data;
mod language;
mod connection;
mod test_runner;

pub use session::Session;
pub use language::{ ModuleLoader, Module, Test, Item };
pub use process::{ Process, ProcessStack, PrimitiveDef };
pub use connection::Connection;
pub use data::{ Value, Type, DataMode, Shape, ShapeVariant, ShapeData };
pub use test_runner::run as run_test;

mod file_io;
mod dumpfile;

pub fn add_primitives(loader: &ModuleLoader) {
    loader.add_primitive_def("file", &file_io::FILE_DEF);
    loader.add_primitive_def("dumpfile", &dumpfile::DUMPFILE_DEF);
    loader.add_primitive_fn("int", &language::FNINT);
    loader.add_primitive_fn("signed", &language::FNSIGNED);
    loader.add_primitive_fn("chunks", &language::FNCHUNKS);
}
<|endoftext|>


<commit_before>//! # What is this?
//!
//! ggez is a Rust library to create a Good Game Easily.
//!
//! More specifically, ggez is a lightweight game framework for making
//! 2D games with minimum friction.  It aims to implement an API based
//! on (a Rustified version of) the [LÖVE](https://love2d.org/) game
//! framework.  This means it contains basic and portable 2D
//! drawing, sound, resource loading and event handling.
//!
//! For a fuller outline, see the [README.md](https://github.com/ggez/ggez/)
//!
//! ## Usage
//!
//! ggez consists of three main parts: A [`Context`](struct.Context.html) object
//! which contains all the state required to interface with the computer's
//! hardware, an [`EventHandler`](event/trait.EventHandler.html) trait that the
//! user implements to register callbacks for events, and various sub-modules such as
//! [`graphics`](graphics/index.html) and [`audio`](audio/index.html) that provide
//! the functionality to actually get stuff done.
//!
//! The general pattern is to create a struct holding your game's data which implements
//! the `EventHandler` trait. Create a new `Context` object with default objects from a
//! [`ContextBuilder`](struct.ContextBuilder.html) or [`Conf`](conf/struct.Conf.html) object,
//! and then call [`event::run()`](event/fn.run.html) with the `Context` and an instance of
//! your `EventHandler` to run your game's main loop.
//!
//! ## Basic Project Template
//!
//! ```rust,no_run
//! use ggez::{Context, ContextBuilder, GameResult};
//! use ggez::event::{self, EventHandler};
//!
//! fn main() {
//!     // Make a Context and an EventLoop.
//!     let (mut ctx, mut event_loop) = /* ContextBuilder params */
//! #       ContextBuilder::new("doc_template", "ggez")
//! #           .build()
//! #           .unwrap();
//!
//!     // Create an instance of your event handler.
//!     // Usually, you should provide it with the Context object to
//!     // use when setting your game up.
//!     let mut my_game = MyGame::new(&mut ctx);
//!
//!     // Run!
//!     match event::run(&mut ctx, &mut event_loop, &mut my_game) {
//!         Ok(_) => println!("Exited cleanly."),
//!         Err(e) => println!("Error occured: {}", e)
//!     }
//! }
//!
//! struct MyGame {
//!     // Your state here...
//! }
//!
//! impl EventHandler for MyGame {
//!     fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
//!         // Update code here...
//! #       Ok(())
//!     }
//!
//!     fn draw(&mut self, _ctx: &mut Context) -> GameResult<()> {
//!         // Draw code here...
//! #       Ok(())
//!     }
//! }
//! #
//! # impl MyGame {
//! #   pub fn new(_ctx: &mut Context) -> MyGame {
//! #       MyGame { }
//! #   }
//! # }
//! ```

#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
// This is not as strong a constraint as `#![forbid(unsafe_code)]` but is good enough.
// It means the only place we use unsafe is then in the modules noted as allowing it.
#![deny(unsafe_code)]
#![warn(bare_trait_objects)]
#![warn(missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate gfx;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate smart_default;

pub extern crate mint;
pub extern crate nalgebra;

pub mod audio;
pub mod conf;
mod context;
pub mod error;
pub mod event;
pub mod filesystem;
pub mod graphics;
pub mod input;
pub mod timer;
mod vfs;

#[cfg(test)]
pub mod tests;

pub use crate::context::{quit, Context, ContextBuilder};
pub use crate::error::*;
<commit_msg>Add graphics::present to basic project template<commit_after>//! # What is this?
//!
//! ggez is a Rust library to create a Good Game Easily.
//!
//! More specifically, ggez is a lightweight game framework for making
//! 2D games with minimum friction.  It aims to implement an API based
//! on (a Rustified version of) the [LÖVE](https://love2d.org/) game
//! framework.  This means it contains basic and portable 2D
//! drawing, sound, resource loading and event handling.
//!
//! For a fuller outline, see the [README.md](https://github.com/ggez/ggez/)
//!
//! ## Usage
//!
//! ggez consists of three main parts: A [`Context`](struct.Context.html) object
//! which contains all the state required to interface with the computer's
//! hardware, an [`EventHandler`](event/trait.EventHandler.html) trait that the
//! user implements to register callbacks for events, and various sub-modules such as
//! [`graphics`](graphics/index.html) and [`audio`](audio/index.html) that provide
//! the functionality to actually get stuff done.
//!
//! The general pattern is to create a struct holding your game's data which implements
//! the `EventHandler` trait. Create a new `Context` object with default objects from a
//! [`ContextBuilder`](struct.ContextBuilder.html) or [`Conf`](conf/struct.Conf.html) object,
//! and then call [`event::run()`](event/fn.run.html) with the `Context` and an instance of
//! your `EventHandler` to run your game's main loop.
//!
//! ## Basic Project Template
//!
//! ```rust,no_run
//! use ggez::{Context, ContextBuilder, GameResult};
//! use ggez::event::{self, EventHandler};
//! use ggez::graphics;
//!
//! fn main() {
//!     // Make a Context and an EventLoop.
//!     let (mut ctx, mut event_loop) = /* ContextBuilder params */
//! #       ContextBuilder::new("doc_template", "ggez")
//! #           .build()
//! #           .unwrap();
//!
//!     // Create an instance of your event handler.
//!     // Usually, you should provide it with the Context object to
//!     // use when setting your game up.
//!     let mut my_game = MyGame::new(&mut ctx);
//!
//!     // Run!
//!     match event::run(&mut ctx, &mut event_loop, &mut my_game) {
//!         Ok(_) => println!("Exited cleanly."),
//!         Err(e) => println!("Error occured: {}", e)
//!     }
//! }
//!
//! struct MyGame {
//!     // Your state here...
//! }
//!
//! impl EventHandler for MyGame {
//!     fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
//!         // Update code here...
//! #       Ok(())
//!     }
//!
//!     fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
//!         // Optional
//!         graphics::clear(ctx, graphics::WHITE);
//!
//!         // Draw code here...
//!
//!         graphics::present(ctx)
//!     }
//! }
//! #
//! # impl MyGame {
//! #   pub fn new(_ctx: &mut Context) -> MyGame {
//! #       MyGame { }
//! #   }
//! # }
//! ```

#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
// This is not as strong a constraint as `#![forbid(unsafe_code)]` but is good enough.
// It means the only place we use unsafe is then in the modules noted as allowing it.
#![deny(unsafe_code)]
#![warn(bare_trait_objects)]
#![warn(missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate gfx;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate smart_default;

pub extern crate mint;
pub extern crate nalgebra;

pub mod audio;
pub mod conf;
mod context;
pub mod error;
pub mod event;
pub mod filesystem;
pub mod graphics;
pub mod input;
pub mod timer;
mod vfs;

#[cfg(test)]
pub mod tests;

pub use crate::context::{quit, Context, ContextBuilder};
pub use crate::error::*;
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate num_traits;

pub mod lapack_binding;
pub mod error;

use ndarray::prelude::*;
use ndarray::LinalgScalar;
use num_traits::float::Float;
use lapack_binding::LapackScalar;
use error::{LinalgError, NotSquareError};

pub trait Vector {
    type Scalar;
    fn norm(&self) -> Self::Scalar;
}

impl<A: Float + LinalgScalar> Vector for Array<A, Ix> {
    type Scalar = A;
    fn norm(&self) -> Self::Scalar {
        self.dot(&self).sqrt()
    }
}

pub trait Matrix: Sized {
    type Scalar;
    type Vector;
    /// number of rows and cols
    fn size(&self) -> (usize, usize);
    fn norm_1(&self) -> Self::Scalar;
    fn norm_i(&self) -> Self::Scalar;
    fn norm_f(&self) -> Self::Scalar;
    // fn svd(self) -> (Self, Self::Vector, Self);
}

pub trait SquareMatrix: Matrix {
    // fn qr(self) -> (Self, Self);
    // fn lu(self) -> (Self, Self);
    // fn eig(self) -> (Self::Vector, Self);
    /// eigenvalue decomposition for Hermite matrix
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError>;
    /// inverse of matrix
    fn inv(self) -> Result<Self, LinalgError>;
    fn check_square(&self) -> Result<(), NotSquareError> {
        let (rows, cols) = self.size();
        if rows == cols {
            Ok(())
        } else {
            Err(NotSquareError {
                rows: rows,
                cols: cols,
            })
        }
    }
}

impl<A: LapackScalar> Matrix for Array<A, (Ix, Ix)> {
    type Scalar = A;
    type Vector = Array<A, Ix>;
    fn size(&self) -> (usize, usize) {
        (self.rows(), self.cols())
    }
    fn norm_1(&self) -> Self::Scalar {
        let (m, n) = self.size();
        let strides = self.strides();
        if strides[0] > strides[1] {
            LapackScalar::norm_i(n, m, self.clone().into_raw_vec())
        } else {
            LapackScalar::norm_1(m, n, self.clone().into_raw_vec())
        }
    }
    fn norm_i(&self) -> Self::Scalar {
        let (m, n) = self.size();
        let strides = self.strides();
        if strides[0] > strides[1] {
            LapackScalar::norm_1(n, m, self.clone().into_raw_vec())
        } else {
            LapackScalar::norm_i(m, n, self.clone().into_raw_vec())
        }
    }
    fn norm_f(&self) -> Self::Scalar {
        let (m, n) = self.size();
        LapackScalar::norm_f(m, n, self.clone().into_raw_vec())
    }
}

impl<A: LapackScalar> SquareMatrix for Array<A, (Ix, Ix)> {
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError> {
        try!(self.check_square());
        let (rows, cols) = self.size();
        let (w, a) = try!(LapackScalar::eigh(rows, self.into_raw_vec()));
        let ea = Array::from_vec(w);
        let va = Array::from_vec(a).into_shape((rows, cols)).unwrap().reversed_axes();
        Ok((ea, va))
    }
    fn inv(self) -> Result<Self, LinalgError> {
        try!(self.check_square());
        let (n, _) = self.size();
        let is_fortran_align = self.strides()[0] > self.strides()[1];
        let a = try!(LapackScalar::inv(n, self.into_raw_vec()));
        let m = Array::from_vec(a).into_shape((n, n)).unwrap();
        if is_fortran_align {
            Ok(m)
        } else {
            Ok(m.reversed_axes())
        }
    }
}
<commit_msg>Implement trace()<commit_after>
extern crate ndarray;
extern crate num_traits;

pub mod lapack_binding;
pub mod error;

use ndarray::prelude::*;
use ndarray::LinalgScalar;
use num_traits::float::Float;
use lapack_binding::LapackScalar;
use error::{LinalgError, NotSquareError};

pub trait Vector {
    type Scalar;
    fn norm(&self) -> Self::Scalar;
}

impl<A: Float + LinalgScalar> Vector for Array<A, Ix> {
    type Scalar = A;
    fn norm(&self) -> Self::Scalar {
        self.dot(&self).sqrt()
    }
}

pub trait Matrix: Sized {
    type Scalar;
    type Vector;
    /// number of rows and cols
    fn size(&self) -> (usize, usize);
    fn norm_1(&self) -> Self::Scalar;
    fn norm_i(&self) -> Self::Scalar;
    fn norm_f(&self) -> Self::Scalar;
    // fn svd(self) -> (Self, Self::Vector, Self);
}

pub trait SquareMatrix: Matrix {
    // fn qr(self) -> (Self, Self);
    // fn lu(self) -> (Self, Self);
    // fn eig(self) -> (Self::Vector, Self);
    /// eigenvalue decomposition for Hermite matrix
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError>;
    /// inverse of matrix
    fn inv(self) -> Result<Self, LinalgError>;
    fn trace(&self) -> Result<Self::Scalar, LinalgError>;
    fn check_square(&self) -> Result<(), NotSquareError> {
        let (rows, cols) = self.size();
        if rows == cols {
            Ok(())
        } else {
            Err(NotSquareError {
                rows: rows,
                cols: cols,
            })
        }
    }
}

impl<A: LapackScalar> Matrix for Array<A, (Ix, Ix)> {
    type Scalar = A;
    type Vector = Array<A, Ix>;
    fn size(&self) -> (usize, usize) {
        (self.rows(), self.cols())
    }
    fn norm_1(&self) -> Self::Scalar {
        let (m, n) = self.size();
        let strides = self.strides();
        if strides[0] > strides[1] {
            LapackScalar::norm_i(n, m, self.clone().into_raw_vec())
        } else {
            LapackScalar::norm_1(m, n, self.clone().into_raw_vec())
        }
    }
    fn norm_i(&self) -> Self::Scalar {
        let (m, n) = self.size();
        let strides = self.strides();
        if strides[0] > strides[1] {
            LapackScalar::norm_1(n, m, self.clone().into_raw_vec())
        } else {
            LapackScalar::norm_i(m, n, self.clone().into_raw_vec())
        }
    }
    fn norm_f(&self) -> Self::Scalar {
        let (m, n) = self.size();
        LapackScalar::norm_f(m, n, self.clone().into_raw_vec())
    }
}

impl<A: LapackScalar> SquareMatrix for Array<A, (Ix, Ix)> {
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError> {
        try!(self.check_square());
        let (rows, cols) = self.size();
        let (w, a) = try!(LapackScalar::eigh(rows, self.into_raw_vec()));
        let ea = Array::from_vec(w);
        let va = Array::from_vec(a).into_shape((rows, cols)).unwrap().reversed_axes();
        Ok((ea, va))
    }
    fn inv(self) -> Result<Self, LinalgError> {
        try!(self.check_square());
        let (n, _) = self.size();
        let is_fortran_align = self.strides()[0] > self.strides()[1];
        let a = try!(LapackScalar::inv(n, self.into_raw_vec()));
        let m = Array::from_vec(a).into_shape((n, n)).unwrap();
        if is_fortran_align {
            Ok(m)
        } else {
            Ok(m.reversed_axes())
        }
    }
    fn trace(&self) -> Result<Self::Scalar, LinalgError> {
        try!(self.check_square());
        let (n, _) = self.size();
        Ok((0..n).fold(A::zero(), |sum, i| sum + self[(i, i)]))
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A [Fleetspeak] client connector library.
//!
//! This library exposes a set of functions for writing client-side Fleetspeak
//! services. Each of these functions operates on a global connection object
//! that is lazily established. If this global connection cannot be established,
//! the library will panic (because without this connection Fleetspeak will shut
//! the service down anyway).
//!
//! Note that each service should send startup information upon its inception
//! and continue to heartbeat from time to time to notify the Fleetspeak client
//! that it did not get stuck.
//!
//! [Fleetspeak]: https://github.com/google/fleetspeak

mod connection;
mod error;

use std::fs::File;
use std::sync::Mutex;
use std::time::Duration;

use lazy_static::lazy_static;
use log::{info, error};

pub use self::connection::Packet;
pub use self::error::{ReadError, WriteError};

/// Sends a heartbeat signal to the Fleetspeak client.
///
/// All client services should heartbeat from time to time. Otherwise, from the
/// Fleetspeak perspective, the service is unresponsive and should be restarted.
///
/// The exact frequency of the required heartbeat is defined in the service
/// configuration file.
pub fn heartbeat() -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::heartbeat(buf))
}

/// Sends a system message with startup information to the Fleetspeak client.
///
/// All clients are required to send this information on startup. If the client
/// does not receive this information quickly enough, the service will be
/// killed.
///
/// The `version` string should contain a self-reported version of the service.
/// This data is used primarily for statistics.
pub fn startup(version: &str) -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::startup(buf, version))
}

/// Sends the message to the Fleetspeak server.
///
/// The message is delivered to the server-side service as specified by the
/// packet and optionally tagged with a type if specified. This optional message
/// type is irrelevant for Fleetspeak but might be useful for the service the
/// message is delivered to.
///
/// In case of any I/O failure or malformed message (e.g. due to encoding
/// problems), an error is reported.
///
/// # Examples
///
/// ```no_run
/// use fleetspeak::Packet;
///
/// fleetspeak::send(Packet {
///     service: String::from("example"),
///     kind: None,
///     data: String::from("Hello, World!"),
/// }).expect("failed to send the packet");
/// ```
pub fn send<M>(packet: Packet<M>) -> Result<(), WriteError>
where
    M: prost::Message,
{
    locked(&CONNECTION.output, |buf| self::connection::send(buf, packet))
}

/// Receives a message from the Fleetspeak server.
///
/// This function will block until there is a message to be read from the input.
/// Note that in particular it means your service will be unable to heartbeat
/// properly. If you are not expecting the message to arrive quickly, you should
/// use [`collect`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`collect`]: fn.collect.html
///
/// # Examples
///
/// ```no_run
/// match fleetspeak::receive::<String>() {
///     Ok(packet) => println!("Hello, {}!", packet.data),
///     Err(error) => eprintln!("failed to receive the packet: {}", error),
/// }
/// ```
pub fn receive<M>() -> Result<Packet<M>, ReadError>
where
    M: prost::Message + Default,
{
    locked(&CONNECTION.input, |buf| self::connection::receive(buf))
}

/// Collects a message from the Fleetspeak server.
///
/// Unlike [`receive`], `collect` will send heartbeat signals at the specified
/// `rate` while waiting for the message.
///
/// This function is useful in the main loop of your service when it is not
/// supposed to do anything until a request from the server arrives. If your
/// service is actually awaiting for a specific message to come, you should
/// use [`receive`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`receive`]: fn.receive.html
///
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
///
/// match fleetspeak::collect::<String>(Duration::from_secs(1)) {
///     Ok(packet) => println!("Hello, {}!", packet.data),
///     Err(error) => eprintln!("failed to collected the packet: {}", error),
/// }
/// ```
pub fn collect<M>(rate: Duration) -> Result<Packet<M>, ReadError>
where
    M: prost::Message + Default + 'static,
{
    // TODO: Refactor this code once `!` stabilizes.
    let (sender, receiver) = std::sync::mpsc::channel();

    std::thread::spawn(move || {
        loop {
            use std::sync::mpsc::TryRecvError::*;

            // The heartbeat thread should stop itself when it receives a signal
            // to do so (or when the channel is closed). Otherwise, it should
            // keep heartbeating.
            match receiver.try_recv() {
                Ok(()) => return,
                Err(Empty) => (),
                Err(Disconnected) => return,
            }

            // Ignoring heartbeat errors is not great, but they can occur only
            // in very rare cases and any subsequent write operations are going
            // to fail soon anyway. Hence, we drop the error on the floor and
            // shut the thread down, hoping that the main thread will notice the
            // problem as soon as it tries to write something. In case the main
            // thread blocks indefinitely, Fleetspeak should figure out that the
            // service is unresponsive and kill it eventually.
            match heartbeat() {
                Ok(()) => (),
                Err(error) => {
                    error!(target: "fleetspeak", "heartbeat error: {}", error);
                    return;
                },
            }

            std::thread::sleep(rate);
        }
    });

    let packet = receive()?;

    // Notify the heartbeat thread to shut down. We do not really care whether
    // the message was really delivered as this can fail only if the channel
    // disconnected (and this can happen only if the thread is already dead).
    let _ = sender.send(());

    Ok(packet)
}

/// A connection to the Fleetspeak client.
///
/// The connection is realized through two files (specified by descriptors given
/// by the Fleetspeak client as environment variables): input and output. Each
/// of these files is guarded by a separate mutex to allow writing (e.g. for
/// sending heartbeat signals) when another thread might be busy with reading
/// messages.
struct Connection {
    input: Mutex<File>,
    output: Mutex<File>,
}

lazy_static! {
    static ref CONNECTION: Connection = {
        let mut input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let mut output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        use self::connection::handshake;
        handshake(&mut input, &mut output).expect("handshake failure");

        info!(target: "fleetspeak", "handshake successful");

        Connection {
            input: Mutex::new(input),
            output: Mutex::new(output),
        }
    };
}

/// Executes the given function with a file extracted from the mutex.
///
/// It might happen that the mutex becomes poisoned and this call will panic in
/// result. This should not be a problem in practice, because mutex poisoning
/// is a result of one of the threads being aborted. In case of a such scenario,
/// it is likely the service needs to be restarted anyway.
fn locked<F, T, E>(mutex: &Mutex<File>, f: F) -> Result<T, E>
where
    F: FnOnce(&mut File) -> Result<T, E>
{
    let mut file = mutex.lock().expect("poisoned connection mutex");
    f(&mut file)
}

/// Opens a file object pointed by an environment variable.
///
/// Note that this function will panic if the environment variable `var` is not
/// a valid file descriptor (in which case the library cannot be initialized and
/// the service is unlikely to work anyway).
fn open(var: &str) -> File {
    let fd = std::env::var(var)
        .expect(&format!("invalid variable `{}`", var))
        .parse()
        .expect(&format!("failed to parse file descriptor"));

    #[cfg(target_family = "unix")]
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }

    #[cfg(target_family = "windows")]
    unsafe {
        // We use `identity` to specify the type for the `parse` call above and
        // then cast it to an appropriate Windows-specific pointer type.
        let handle = std::convert::identity::<usize>(fd) as _;
        std::os::windows::io::FromRawHandle::from_raw_handle(handle)
    }
}
<commit_msg>Create a heartbeat variant with throttling.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A [Fleetspeak] client connector library.
//!
//! This library exposes a set of functions for writing client-side Fleetspeak
//! services. Each of these functions operates on a global connection object
//! that is lazily established. If this global connection cannot be established,
//! the library will panic (because without this connection Fleetspeak will shut
//! the service down anyway).
//!
//! Note that each service should send startup information upon its inception
//! and continue to heartbeat from time to time to notify the Fleetspeak client
//! that it did not get stuck.
//!
//! [Fleetspeak]: https://github.com/google/fleetspeak

mod connection;
mod error;

use std::fs::File;
use std::sync::Mutex;
use std::time::{Duration, Instant};

use lazy_static::lazy_static;
use log::{info, error};

pub use self::connection::Packet;
pub use self::error::{ReadError, WriteError};

/// Sends a heartbeat signal to the Fleetspeak client.
///
/// All client services should heartbeat from time to time. Otherwise, from the
/// Fleetspeak perspective, the service is unresponsive and should be restarted.
///
/// The exact frequency of the required heartbeat is defined in the service
/// configuration file.
pub fn heartbeat() -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::heartbeat(buf))
}

/// Sends a heartbeat signal to the Fleetspeak client but no more frequently
/// than the specified `rate`.
///
/// Note that the specified `rate` should be at least the rate defined in the
/// Fleetspeak service configuration file. Because of potential slowdowns, some
/// margin of error should be left.
///
/// See documentation for the [`heartbeat`] function for more details.
///
/// [`heartbeat`]: heartbeat
pub fn heartbeat_with_throttle(rate: Duration) -> Result<(), WriteError> {
    lazy_static! {
        static ref LAST_HEARTBEAT: Mutex<Option<Instant>> = Mutex::new(None);
    }

    let mut last_heartbeat = LAST_HEARTBEAT.lock()
        .expect("poisoned heartbeat mutex");

    match *last_heartbeat {
        Some(last_heartbeat) if last_heartbeat.elapsed() < rate => {
            // Do nothing if the last heartbeat happened more recently than the
            // specified heartbeat rate.
            return Ok(());
        }
        _ => (),
    }

    heartbeat()?;
    *last_heartbeat = Some(Instant::now());

    Ok(())
}

/// Sends a system message with startup information to the Fleetspeak client.
///
/// All clients are required to send this information on startup. If the client
/// does not receive this information quickly enough, the service will be
/// killed.
///
/// The `version` string should contain a self-reported version of the service.
/// This data is used primarily for statistics.
pub fn startup(version: &str) -> Result<(), WriteError> {
    locked(&CONNECTION.output, |buf| self::connection::startup(buf, version))
}

/// Sends the message to the Fleetspeak server.
///
/// The message is delivered to the server-side service as specified by the
/// packet and optionally tagged with a type if specified. This optional message
/// type is irrelevant for Fleetspeak but might be useful for the service the
/// message is delivered to.
///
/// In case of any I/O failure or malformed message (e.g. due to encoding
/// problems), an error is reported.
///
/// # Examples
///
/// ```no_run
/// use fleetspeak::Packet;
///
/// fleetspeak::send(Packet {
///     service: String::from("example"),
///     kind: None,
///     data: String::from("Hello, World!"),
/// }).expect("failed to send the packet");
/// ```
pub fn send<M>(packet: Packet<M>) -> Result<(), WriteError>
where
    M: prost::Message,
{
    locked(&CONNECTION.output, |buf| self::connection::send(buf, packet))
}

/// Receives a message from the Fleetspeak server.
///
/// This function will block until there is a message to be read from the input.
/// Note that in particular it means your service will be unable to heartbeat
/// properly. If you are not expecting the message to arrive quickly, you should
/// use [`collect`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`collect`]: fn.collect.html
///
/// # Examples
///
/// ```no_run
/// match fleetspeak::receive::<String>() {
///     Ok(packet) => println!("Hello, {}!", packet.data),
///     Err(error) => eprintln!("failed to receive the packet: {}", error),
/// }
/// ```
pub fn receive<M>() -> Result<Packet<M>, ReadError>
where
    M: prost::Message + Default,
{
    locked(&CONNECTION.input, |buf| self::connection::receive(buf))
}

/// Collects a message from the Fleetspeak server.
///
/// Unlike [`receive`], `collect` will send heartbeat signals at the specified
/// `rate` while waiting for the message.
///
/// This function is useful in the main loop of your service when it is not
/// supposed to do anything until a request from the server arrives. If your
/// service is actually awaiting for a specific message to come, you should
/// use [`receive`] instead.
///
/// In case of any I/O failure or malformed message (e.g. due to parsing issues
/// or when some fields are not being present), an error is reported.
///
/// [`receive`]: fn.receive.html
///
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
///
/// match fleetspeak::collect::<String>(Duration::from_secs(1)) {
///     Ok(packet) => println!("Hello, {}!", packet.data),
///     Err(error) => eprintln!("failed to collected the packet: {}", error),
/// }
/// ```
pub fn collect<M>(rate: Duration) -> Result<Packet<M>, ReadError>
where
    M: prost::Message + Default + 'static,
{
    // TODO: Refactor this code once `!` stabilizes.
    let (sender, receiver) = std::sync::mpsc::channel();

    std::thread::spawn(move || {
        loop {
            use std::sync::mpsc::TryRecvError::*;

            // The heartbeat thread should stop itself when it receives a signal
            // to do so (or when the channel is closed). Otherwise, it should
            // keep heartbeating.
            match receiver.try_recv() {
                Ok(()) => return,
                Err(Empty) => (),
                Err(Disconnected) => return,
            }

            // Ignoring heartbeat errors is not great, but they can occur only
            // in very rare cases and any subsequent write operations are going
            // to fail soon anyway. Hence, we drop the error on the floor and
            // shut the thread down, hoping that the main thread will notice the
            // problem as soon as it tries to write something. In case the main
            // thread blocks indefinitely, Fleetspeak should figure out that the
            // service is unresponsive and kill it eventually.
            match heartbeat() {
                Ok(()) => (),
                Err(error) => {
                    error!(target: "fleetspeak", "heartbeat error: {}", error);
                    return;
                },
            }

            std::thread::sleep(rate);
        }
    });

    let packet = receive()?;

    // Notify the heartbeat thread to shut down. We do not really care whether
    // the message was really delivered as this can fail only if the channel
    // disconnected (and this can happen only if the thread is already dead).
    let _ = sender.send(());

    Ok(packet)
}

/// A connection to the Fleetspeak client.
///
/// The connection is realized through two files (specified by descriptors given
/// by the Fleetspeak client as environment variables): input and output. Each
/// of these files is guarded by a separate mutex to allow writing (e.g. for
/// sending heartbeat signals) when another thread might be busy with reading
/// messages.
struct Connection {
    input: Mutex<File>,
    output: Mutex<File>,
}

lazy_static! {
    static ref CONNECTION: Connection = {
        let mut input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let mut output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        use self::connection::handshake;
        handshake(&mut input, &mut output).expect("handshake failure");

        info!(target: "fleetspeak", "handshake successful");

        Connection {
            input: Mutex::new(input),
            output: Mutex::new(output),
        }
    };
}

/// Executes the given function with a file extracted from the mutex.
///
/// It might happen that the mutex becomes poisoned and this call will panic in
/// result. This should not be a problem in practice, because mutex poisoning
/// is a result of one of the threads being aborted. In case of a such scenario,
/// it is likely the service needs to be restarted anyway.
fn locked<F, T, E>(mutex: &Mutex<File>, f: F) -> Result<T, E>
where
    F: FnOnce(&mut File) -> Result<T, E>
{
    let mut file = mutex.lock().expect("poisoned connection mutex");
    f(&mut file)
}

/// Opens a file object pointed by an environment variable.
///
/// Note that this function will panic if the environment variable `var` is not
/// a valid file descriptor (in which case the library cannot be initialized and
/// the service is unlikely to work anyway).
fn open(var: &str) -> File {
    let fd = std::env::var(var)
        .expect(&format!("invalid variable `{}`", var))
        .parse()
        .expect(&format!("failed to parse file descriptor"));

    #[cfg(target_family = "unix")]
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }

    #[cfg(target_family = "windows")]
    unsafe {
        // We use `identity` to specify the type for the `parse` call above and
        // then cast it to an appropriate Windows-specific pointer type.
        let handle = std::convert::identity::<usize>(fd) as _;
        std::os::windows::io::FromRawHandle::from_raw_handle(handle)
    }
}
<|endoftext|>