<commit_before>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use french_press::alloc::AllocBox;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_obj::JsObjStruct;
use js_types::js_str::JsStrStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum, JsKey, JsPtrTag};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

use unescape::unescape;

// Helper to avoid repeating this everywhere
fn scalar(v: JsType) -> (JsVar, Option<JsPtrEnum>) {
    (JsVar::new(v), None)
}

/// Evaluate a string containing some JavaScript statements (or sequences of statements).
/// Returns a JsVar which is the return value of those statements.
pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    println!("{}", string);
    match parse_Stmt(string) {
        Ok(stmt) => {
            eval_stmt(&stmt, state).0
        }
        Err(e) => panic!("parse error: {:?}", e),
    }
}

/// Evaluate a single JS statement (which may be a block or sequence of statements).
/// Returns tuple of (evaluated final value, return value), where return value requires that
/// `return` be used to generate it.
pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        // var_string = exp;
        Assign(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());

            // Clone the js_var to store into the ScopeManager
            let cloned = js_var.clone();
            match state.alloc(cloned, js_ptr) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }

            (js_var, None)
        },

        // exp;
        BareExp(ref exp) => (eval_exp(exp, &mut state).0, None),

        // var var_string = exp
        Decl(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());
            match state.alloc(js_var.clone(), js_ptr) {
                Ok(_) => (js_var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },

        // if (condition) { if_block } else { else_block }
        If(ref condition, ref if_block, ref else_block) => {
            // evaluate expression
            if eval_exp(&condition, state).0.as_bool() {
                // condition = true, evaluate if-block.
                eval_stmt(&*if_block, state)
            } else {
                // condition = false
                // evaluate else-block if it exists, otherwise return undefined.
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },

        // return exp
        Ret(ref exp) => {
            let js_var = eval_exp(&exp, &mut state).0;
            (js_var.clone(), Some(js_var))
        }

        // a sequence of any two expressions
        Seq(ref s1, ref s2) => {
            eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },

        // throw <expression>;
        Throw(..) => unimplemented!(),

        // try { block } [catch <expression> { block} &&/|| finally { block }]
        Try(..) => unimplemented!(),

        // while (condition) { block }
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).0.as_bool() {
                    // TODO: check to see if a return stmt has been reached.
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    // condition is no longer true, return a return value
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

/// Evaluate an expression into a JsVar.
pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> (JsVar, Option<JsPtrEnum>) {
    match e {
        // e1 [op] e2
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state).0;
            let val2 = eval_exp(e2, state).0;

            match *op {
                And => scalar(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => scalar(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => scalar(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => scalar(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => scalar(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => scalar(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => scalar(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => scalar(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => scalar(JsNum(val1.as_number() - val2.as_number())),
                Plus  => scalar(JsNum(val1.as_number() + val2.as_number())),
                Slash => scalar(JsNum(val1.as_number() / val2.as_number())),
                Star  => scalar(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => scalar(JsBool(b)),

        // fun_name([arg_exp1, arg_exps])
        &Call(ref fun_name, ref arg_exps) => {
            let fun_binding = eval_exp(fun_name, state).0;

            // Create vector of arguments, evaluated to JsVars.
            let mut args = Vec::new();
            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            match state.load(&fun_binding.binding) {
                Ok((_, Some(JsPtrEnum::JsFn(js_fn_struct)))) => {
                    state.push_scope(e);

                    for param in js_fn_struct.params {
                        let mut arg = if args.is_empty() {
                            scalar(JsUndef)
                        } else {
                            args.remove(0)
                        };

                        arg.0.binding = Binding::new(param.to_owned());
                        state.alloc(arg.0, arg.1)
                            .expect("Unable to store function argument in scope");
                    }

                    let (_, v) = eval_stmt(&js_fn_struct.stmt, state);

                    // Should we yield here? Not sure, so for now it doesn't
                    state.pop_scope(false).expect("Unable to clear scope for function");
                    // TODO handle obj
                    v.map(|x| (x, None)).unwrap_or(scalar(JsUndef))
                }
                Ok(_) => panic!("Invalid call object"),
                _ => panic!("ReferenceError: {:?} is not defined", fun_name)
            }
        },

        // function([param1, params]) { body }
        // function opt_binding([param1, params]) { body }
        &Defun(ref opt_binding, ref params, ref body) => {
            let js_fun = JsFnStruct::new(opt_binding, params, &**body);
            let mut var = JsVar::new(JsPtr(JsPtrTag::JsFn));

            if let &Some(ref s) = opt_binding {
                var.binding = Binding::new(s.to_owned());
            }

            if let Err(_) = state.alloc(var.clone(), Some(JsPtrEnum::JsFn(js_fun.clone()))) {
                panic!("error storing function into state");
            }

            (var, Some(JsPtrEnum::JsFn(js_fun)))
        },

        // var.binding
        &InstanceVar(ref instance_exp, ref var) => {
            // TODO: this needs better type-reasoning and errors
            let (instance_var, var_ptr) = eval_exp(instance_exp, state);
            if let JsPtr(_) = instance_var.t {
                match var_ptr {
                    Some(JsPtrEnum::JsObj(obj_struct)) => {
                        let try_inner = obj_struct.dict.get(&JsKey::JsStr(JsStrStruct::new(var)));
                        if let Some(inner_var) = try_inner {
                            return (inner_var.clone(), None);
                        } else {
                            return scalar(JsUndef);
                        }
                    },
                    // TODO: all JsPtrs can have instance vars/methods, not just JsObjs
                    _ => unimplemented!()
                }
            } else {
                // TODO: Things which are not ptrs can also have instance vars/methods
                unimplemented!()
            }
        },

        &Null => {
            scalar(JsNull)
        },

        &Float(f) => scalar(JsType::JsNum(f)),
        &Neg(ref exp) => scalar(JsNum(-eval_exp(exp, state).0.as_number())),
        &Pos(ref exp) => scalar(JsNum(eval_exp(exp, state).0.as_number())),

        &PostDec(ref exp) => (eval_float_post_op!(exp, f, f - 1.0, state), None),
        &PostInc(ref exp) => (eval_float_post_op!(exp, f, f + 1.0, state), None),
        &PreDec(ref exp)  => (eval_float_pre_op!(exp, f, f - 1.0, state),  None),
        &PreInc(ref exp)  => (eval_float_pre_op!(exp, f, f + 1.0, state),  None),

        &NewObject(_, _) => unimplemented!(),
        &Object(ref fields) => {
            let mut kv_tuples = Vec::new();
            for f in fields {
                let f_key = JsKey::JsStr(JsStrStruct::new(&f.0));
                // TODO: handle obj as key/value pair
                let f_exp = eval_exp(&*f.1, state).0;
                kv_tuples.push((f_key, f_exp, None));
            }
            let obj = JsObjStruct::new(None, "", kv_tuples, &mut AllocBox::new());
            (JsVar::new(JsPtr(JsPtrTag::JsObj)), Some(JsPtrEnum::JsObj(obj)))
        },

        &Str(ref s) => {
            let var = JsVar::new(JsPtr(JsPtrTag::JsStr));
            let ptr = Some(JsPtrEnum::JsStr(JsStrStruct::new(&unescape(s).expect("Invalid string"))));
            (var, ptr)
        }
        &TypeOf(ref e) => (JsVar::new(JsPtr(JsPtrTag::JsStr)), Some(JsPtrEnum::JsStr(JsStrStruct::new(&eval_exp(e, state).0.type_of())))),
        &Undefined => scalar(JsUndef),
        &Var(ref var_binding) => {
            state.load(&Binding::new(var_binding.clone()))
                .expect("ReferenceError: {} is not defined")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<commit_msg>fix issue with function calls<commit_after>#[macro_use]
mod macros;

use coerce::{AsBool,AsNumber};

use french_press::ScopeManager;
use french_press::alloc::AllocBox;
use js_types::binding::Binding;
use js_types::js_fn::JsFnStruct;
use js_types::js_obj::JsObjStruct;
use js_types::js_str::JsStrStruct;
use js_types::js_var::{JsVar, JsType, JsPtrEnum, JsKey, JsPtrTag};
use js_types::js_var::JsType::*;

use jsrs_parser::lalr::parse_Stmt;
use jsrs_common::ast::*;
use jsrs_common::ast::Exp::*;
use jsrs_common::ast::BinOp::*;
use jsrs_common::ast::Stmt::*;

use unescape::unescape;

// Helper to avoid repeating this everywhere
fn scalar(v: JsType) -> (JsVar, Option<JsPtrEnum>) {
    (JsVar::new(v), None)
}

/// Evaluate a string containing some JavaScript statements (or sequences of statements).
/// Returns a JsVar which is the return value of those statements.
pub fn eval_string(string: &str, state: &mut ScopeManager) -> JsVar {
    println!("{}", string);
    match parse_Stmt(string) {
        Ok(stmt) => {
            eval_stmt(&stmt, state).0
        }
        Err(e) => panic!("parse error: {:?}", e),
    }
}

/// Evaluate a single JS statement (which may be a block or sequence of statements).
/// Returns tuple of (evaluated final value, return value), where return value requires that
/// `return` be used to generate it.
pub fn eval_stmt(s: &Stmt, mut state: &mut ScopeManager) -> (JsVar, Option<JsVar>) {
    match *s {
        // var_string = exp;
        Assign(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());

            // Clone the js_var to store into the ScopeManager
            let cloned = js_var.clone();
            match state.alloc(cloned, js_ptr) {
                Ok(_) => (),
                e @ Err(_) => println!("{:?}", e),
            }

            (js_var, None)
        },

        // exp;
        BareExp(ref exp) => (eval_exp(exp, &mut state).0, None),

        // var var_string = exp
        Decl(ref var_string, ref exp) => {
            let (mut js_var, js_ptr) = eval_exp(exp, state);
            js_var.binding = Binding::new(var_string.clone());
            match state.alloc(js_var.clone(), js_ptr) {
                Ok(_) => (js_var, None),
                e @ Err(_) => panic!("{:?}", e),
            }
        },

        // if (condition) { if_block } else { else_block }
        If(ref condition, ref if_block, ref else_block) => {
            // evaluate expression
            if eval_exp(&condition, state).0.as_bool() {
                // condition = true, evaluate if-block.
                eval_stmt(&*if_block, state)
            } else {
                // condition = false
                // evaluate else-block if it exists, otherwise return undefined.
                if let Some(ref block) = *else_block {
                    eval_stmt(&*block, state)
                } else {
                    (JsVar::new(JsUndef), None)
                }
            }
        },

        // return exp
        Ret(ref exp) => {
            let js_var = eval_exp(&exp, &mut state).0;
            (js_var.clone(), Some(js_var))
        }

        // a sequence of any two expressions
        Seq(ref s1, ref s2) => {
            eval_stmt(&*s1, &mut state);
            eval_stmt(&*s2, &mut state)
        },

        // throw <expression>;
        Throw(..) => unimplemented!(),

        // try { block } [catch <expression> { block} &&/|| finally { block }]
        Try(..) => unimplemented!(),

        // while (condition) { block }
        While(ref condition, ref block) => {
            let mut ret_val = None;
            loop {
                if eval_exp(&condition, state).0.as_bool() {
                    // TODO: check to see if a return stmt has been reached.
                    let (_, v) = eval_stmt(&*block, state);
                    ret_val = v;
                } else {
                    // condition is no longer true, return a return value
                    return (JsVar::new(JsUndef), ret_val);
                }
            }
        }
    }
}

/// Evaluate an expression into a JsVar.
pub fn eval_exp(e: &Exp, mut state: &mut ScopeManager) -> (JsVar, Option<JsPtrEnum>) {
    match e {
        // e1 [op] e2
        &BinExp(ref e1, ref op, ref e2) => {
            let val1 = eval_exp(e1, state).0;
            let val2 = eval_exp(e2, state).0;

            match *op {
                And => scalar(JsBool(val1.as_bool() && val2.as_bool())),
                Or  => scalar(JsBool(val1.as_bool() || val2.as_bool())),

                Ge  => scalar(JsBool(val1.as_bool() >= val2.as_bool())),
                Gt  => scalar(JsBool(val1.as_bool() >  val2.as_bool())),
                Le  => scalar(JsBool(val1.as_bool() <= val2.as_bool())),
                Lt  => scalar(JsBool(val1.as_bool() <  val2.as_bool())),
                Neq => scalar(JsBool(val1.as_bool() != val2.as_bool())),
                Eql => scalar(JsBool(val1.as_bool() == val2.as_bool())),

                Minus => scalar(JsNum(val1.as_number() - val2.as_number())),
                Plus  => scalar(JsNum(val1.as_number() + val2.as_number())),
                Slash => scalar(JsNum(val1.as_number() / val2.as_number())),
                Star  => scalar(JsNum(val1.as_number() * val2.as_number())),
            }
        }
        &Bool(b) => scalar(JsBool(b)),

        // fun_name([arg_exp1, arg_exps])
        &Call(ref fun_name, ref arg_exps) => {
            let (fun_binding, fun_ptr) = eval_exp(fun_name, state);

            // Create vector of arguments, evaluated to JsVars.
            let mut args = Vec::new();
            for exp in arg_exps {
                args.push(eval_exp(exp, state));
            }

            let js_fn_struct = match fun_ptr {
                Some(JsPtrEnum::JsFn(fun)) => fun,
                Some(_) => panic!("ReferenceError: {:?} is not a function", fun_name),
                None => match state.load(&fun_binding.binding) {
                    Ok((_, Some(JsPtrEnum::JsFn(fun)))) => fun,
                    Ok(_) => panic!("ReferenceError: {:?} is not a function", fun_name),
                    Err(_) => panic!("ReferenceError: {:?} is not defined", fun_name),
                }
            };

            state.push_scope(e);

            for param in js_fn_struct.params {
                let mut arg = if args.is_empty() {
                    scalar(JsUndef)
                } else {
                    args.remove(0)
                };

                arg.0.binding = Binding::new(param.to_owned());
                state.alloc(arg.0, arg.1)
                .expect("Unable to store function argument in scope");
            }

            let (_, v) = eval_stmt(&js_fn_struct.stmt, state);

            // Should we yield here? Not sure, so for now it doesn't
            state.pop_scope(false).expect("Unable to clear scope for function");
            // TODO handle obj
            v.map(|x| (x, None)).unwrap_or(scalar(JsUndef))
        }

        // function([param1, params]) { body }
        // function opt_binding([param1, params]) { body }
        &Defun(ref opt_binding, ref params, ref body) => {
            let js_fun = JsFnStruct::new(opt_binding, params, &**body);
            let mut var = JsVar::new(JsPtr(JsPtrTag::JsFn));

            if let &Some(ref s) = opt_binding {
                var.binding = Binding::new(s.to_owned());
            }

            if let Err(_) = state.alloc(var.clone(), Some(JsPtrEnum::JsFn(js_fun.clone()))) {
                panic!("error storing function into state");
            }

            (var, Some(JsPtrEnum::JsFn(js_fun)))
        },

        // var.binding
        &InstanceVar(ref instance_exp, ref var) => {
            // TODO: this needs better type-reasoning and errors
            let (instance_var, var_ptr) = eval_exp(instance_exp, state);
            if let JsPtr(_) = instance_var.t {
                match var_ptr {
                    Some(JsPtrEnum::JsObj(obj_struct)) => {
                        let try_inner = obj_struct.dict.get(&JsKey::JsStr(JsStrStruct::new(var)));
                        if let Some(inner_var) = try_inner {
                            return (inner_var.clone(), None);
                        } else {
                            return scalar(JsUndef);
                        }
                    },
                    // TODO: all JsPtrs can have instance vars/methods, not just JsObjs
                    _ => unimplemented!()
                }
            } else {
                // TODO: Things which are not ptrs can also have instance vars/methods
                unimplemented!()
            }
        },

        &Null => {
            scalar(JsNull)
        },

        &Float(f) => scalar(JsType::JsNum(f)),
        &Neg(ref exp) => scalar(JsNum(-eval_exp(exp, state).0.as_number())),
        &Pos(ref exp) => scalar(JsNum(eval_exp(exp, state).0.as_number())),

        &PostDec(ref exp) => (eval_float_post_op!(exp, f, f - 1.0, state), None),
        &PostInc(ref exp) => (eval_float_post_op!(exp, f, f + 1.0, state), None),
        &PreDec(ref exp)  => (eval_float_pre_op!(exp, f, f - 1.0, state),  None),
        &PreInc(ref exp)  => (eval_float_pre_op!(exp, f, f + 1.0, state),  None),

        &NewObject(_, _) => unimplemented!(),
        &Object(ref fields) => {
            let mut kv_tuples = Vec::new();
            for f in fields {
                let f_key = JsKey::JsStr(JsStrStruct::new(&f.0));
                // TODO: handle obj as key/value pair
                let f_exp = eval_exp(&*f.1, state).0;
                kv_tuples.push((f_key, f_exp, None));
            }
            let obj = JsObjStruct::new(None, "", kv_tuples, &mut AllocBox::new());
            (JsVar::new(JsPtr(JsPtrTag::JsObj)), Some(JsPtrEnum::JsObj(obj)))
        },

        &Str(ref s) => {
            let var = JsVar::new(JsPtr(JsPtrTag::JsStr));
            let ptr = Some(JsPtrEnum::JsStr(JsStrStruct::new(&unescape(s).expect("Invalid string"))));
            (var, ptr)
        }
        &TypeOf(ref e) => (JsVar::new(JsPtr(JsPtrTag::JsStr)), Some(JsPtrEnum::JsStr(JsStrStruct::new(&eval_exp(e, state).0.type_of())))),
        &Undefined => scalar(JsUndef),
        &Var(ref var_binding) => {
            state.load(&Binding::new(var_binding.clone()))
                .expect("ReferenceError: {} is not defined")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::collections::hash_set::HashSet;
    use french_press::init_gc;
    use js_types::js_var::JsType;
    use js_types::binding::Binding;

    #[test]
    fn test_eval_literals() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(5.0f64), eval_string("5.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.0f64), eval_string("0.0;\n", &mut state).t);
        assert_eq!(JsType::JsUndef, eval_string("undefined;\n", &mut state).t);
    }

    //// TODO: handle `var` and no `var` separately
    //#[test]
    //fn test_store_state() {
    //    let mut state = HashMap::new();
    //    assert_eq!(JsUndefined, eval_string("var a = 1;\n", &mut state));
    //    assert_eq!(JsNumber(2.0f64), eval_string("a = 2;\n", &mut state));
    //    assert_eq!(JsUndefined, eval_string("var b = 3;\n", &mut state));
    //    assert_eq!(JsNumber(4.0f64), eval_string("c = 4;\n", &mut state));
    //}

    #[test]
    fn test_inc_dec() {
        let mut state = init_gc();
        //assert_eq!(JsType::JsNum(1.0f64), eval_string("var a = 1;\n", &mut state).t);
        //assert_eq!(&JsType::JsNum(1.0), state.load(&Binding::new("a")).unwrap());

        //assert_eq!(JsType::JsNum(1.0f64), eval_string("a++;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("++a;\n", &mut state));
        //assert_eq!(&JsNumber(3.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(3.0f64), eval_string("a--;\n", &mut state));
        //assert_eq!(&JsNumber(2.0f64), state.get("a").unwrap());

        //assert_eq!(JsNumber(1.0f64), eval_string("--a;\n", &mut state));
        //assert_eq!(&JsNumber(1.0f64), state.get("a").unwrap());
    }

    #[test]
    fn test_binexp() {
        let mut state = init_gc();
        assert_eq!(JsType::JsNum(6.0f64),  eval_string("2.0 + 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(0.5f64),  eval_string("2.0 / 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(-2.0f64), eval_string("2.0 - 4.0;\n", &mut state).t);
        assert_eq!(JsType::JsNum(8.0f64),  eval_string("2.0 * 4.0;\n", &mut state).t);
    }
}
<|endoftext|>


<commit_before>pub mod stats;

mod scrape;

use crate::config::Config;
use crate::err::{self, Result};
use notify_rust::Notification;
use once_cell::sync::Lazy;
use serde::de::Visitor;
use serde::{Deserialize, Deserializer};
use snafu::ResultExt;
use stats::ListenerStats;
use std::borrow::Cow;
use std::cmp::{self, Eq, Ord};
use std::fmt;
use std::result;
use std::str::FromStr;
use strum_macros::EnumString;

#[derive(Debug)]
pub struct Feed<'a> {
    pub id: u32,
    pub name: String,
    pub listeners: u32,
    pub location: Location,
    pub county: Cow<'a, str>,
    pub alert: Option<String>,
}

impl<'a> Feed<'a> {
    pub fn scrape_all(config: &Config) -> Result<Vec<Self>> {
        let mut feeds = Self::scrape_source(Source::Top50, config.misc.minimum_listeners)?;

        if let Some(loc) = config.misc.location {
            let loc_feeds =
                Self::scrape_source(Source::Location(loc), config.misc.minimum_listeners)?;

            feeds.extend(loc_feeds);
        }

        feeds.sort_unstable();
        feeds.dedup();

        Ok(feeds)
    }

    fn scrape_source(source: Source, min_listeners: u32) -> Result<Vec<Self>> {
        static REQ_AGENT: Lazy<ureq::Agent> = Lazy::new(|| {
            let mut agent = ureq::agent();
            agent.set("Connection", "keep-alive");
            agent
        });

        let resp = REQ_AGENT
            .get(source.url().as_ref())
            .timeout_connect(15_000)
            .timeout_read(15_000)
            .call();

        if let Some(err) = resp.synthetic_error() {
            return Err(err.into());
        }

        let body = resp.into_string().context(err::HttpIO)?;

        match source {
            Source::Top50 => scrape::scrape_top(&body, min_listeners).context(err::ParseTopFeeds),
            Source::Location(location) => scrape::scrape_location(&body, min_listeners, location)
                .context(err::ParseLocationFeeds { location }),
        }
    }
}

impl<'a> PartialEq for Feed<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl<'a> Eq for Feed<'a> {}

impl<'a> PartialOrd for Feed<'a> {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<'a> Ord for Feed<'a> {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.id.cmp(&other.id)
    }
}

pub enum Source {
    Top50,
    Location(Location),
}

impl Source {
    pub fn url(&self) -> Cow<str> {
        match self {
            Self::Top50 => "https://www.broadcastify.com/listen/top".into(),
            Self::Location(loc) => {
                format!("https://www.broadcastify.com/listen/stid/{}", loc.id()).into()
            }
        }
    }
}

#[derive(Debug)]
pub struct FeedNotif<'a> {
    pub feed: Feed<'a>,
    pub jump: f32,
}

impl<'a> FeedNotif<'a> {
    pub fn new(feed: Feed<'a>, stats: &ListenerStats) -> Self {
        Self {
            feed,
            jump: stats.jump,
        }
    }

    pub fn show_notif(&self, index: u32, max_index: u32) -> Result<()> {
        let title = format!(
            concat!(env!("CARGO_PKG_NAME"), " update {} of {}"),
            index, max_index
        );

        let alert = match &self.feed.alert {
            Some(alert) => Cow::Owned(format!("\nalert: {}", alert)),
            None => Cow::Borrowed(""),
        };

        let body = format!(
            "{abbrev} | {name}\n{listeners} (^{jump}){alert}",
            abbrev = self.feed.location.abbrev(),
            name = self.feed.name,
            listeners = self.feed.listeners,
            jump = self.jump as i32,
            alert = alert,
        );

        Notification::new()
            .summary(&title)
            .body(&body)
            .show()
            .context(err::CreateNotif)
            .map(|_| ())
    }

    pub fn sort_all(notifs: &mut [Self], config: &Config) {
        use crate::config::{SortOrder, SortType};

        notifs.sort_unstable_by(|x, y| {
            let (x, y) = match config.sorting.order {
                SortOrder::Ascending => (x, y),
                SortOrder::Descending => (y, x),
            };

            match config.sorting.value {
                SortType::Listeners => x.feed.listeners.cmp(&y.feed.listeners),
                SortType::Jump => {
                    let x_jump = x.jump as i32;
                    let y_jump = y.jump as i32;

                    x_jump.cmp(&y_jump)
                }
            }
        });
    }

    pub fn show_all(notifs: &[Self]) -> Result<()> {
        let num_notifs = notifs.len() as u32;

        for (i, notif) in notifs.iter().enumerate() {
            notif.show_notif(1 + i as u32, num_notifs)?;
        }

        Ok(())
    }
}

/// List of all states / provinces / territories on Broadcastify that have a significant feed presence or airport feeds.
/// Every location can be mapped to its state ID as it appears on Broadcastify.
///
/// Airport feeds are used as a factor for being on this list because there's a chance of a major event occuring on them, at least more so than just amateur radio stations.
#[derive(Copy, Clone, Debug, EnumString, Eq, FromPrimitive, Hash, PartialEq)]
#[strum(serialize_all = "kebab_case")]
#[repr(u32)]
pub enum Location {
    // United States
    UsAlabama = 1,
    UsAlaska,
    UsArizona = 4,
    UsArkansas,
    UsCalifornia,
    UsColorado = 8,
    UsConnecticut,
    UsDelaware,
    UsDistrictOfColumbia,
    UsFlorida,
    UsGeorgia,
    UsHawaii = 15,
    UsIdaho,
    UsIllinois,
    UsIndiana,
    UsIowa,
    UsKansas,
    UsKentucky,
    UsLouisiana,
    UsMaine,
    UsMaryland,
    UsMassachusetts,
    UsMichigan,
    UsMinnesota,
    UsMississippi,
    UsMissouri,
    UsMontana,
    UsNebraska,
    UsNevada,
    UsNewHampshire,
    UsNewJersey,
    UsNewMexico,
    UsNewYork,
    UsNorthCarolina,
    UsNorthDakota,
    UsOhio,
    UsOklahoma,
    UsOregon,
    UsPennsylvania,
    UsRhodeIsland = 44,
    UsSouthCarolina,
    UsSouthDakota,
    UsTennessee,
    UsTexas,
    UsUtah,
    UsVermont,
    UsVirginia,
    UsWashington = 53,
    UsWestVirginia,
    UsWisconsin,
    UsWyoming,
    // Canada
    CaAlberta = 101,
    CaBritishColumbia,
    CaManitoba,
    CaNewBrunswick,
    CaNewfoundland,
    CaNorthwestTerritories,
    CaNovaScotia,
    CaNunavut,
    CaOntario,
    CaPrinceEdwardIsland,
    CaQuebec,
    CaSaskatchewan,
    CaYukon,
    // Austrailia
    AuWesternAustrailia = 151,
    AuVictoria,
    AuSouthAustrailia,
    AuNorthernTerritory,
    AuQueensland,
    AuTasmania,
    AuNewSouthWales,
    AuAustralianCapitalTerritory,
    // Netherlands
    NlCountrywide = 223,
    NlDrenthe = 688,
    NlFlevoland,
    NlFriesland,
    NlGelderland,
    NlGroningen,
    NlLimburg,
    NlNoordBrabant,
    NlNoordHolland,
    NlOverijssel,
    NlUtrecht,
    NlZeeland,
    NlZuidHolland,
    // Malaysia
    MyCountrywide = 231,
    // Luxembourg
    LuCountrywide = 252,
    // Brazil
    BrCountrywide = 345,
    // Chile
    ClValparaiso = 714,
    ClBiobio = 717,
    ClAraucania,
    ClSantiago = 723,
}

impl Location {
    const LISTED_COUNTRIES: [&'static str; 8] = [
        "Austrailia",
        "Brazil",
        "Canada",
        "Chile",
        "Luxembourg",
        "Malaysia",
        "Netherlands",
        "United States",
    ];

    #[inline(always)]
    pub fn id(self) -> u32 {
        self as u32
    }

    pub fn abbrev(self) -> &'static str {
        match self {
            // United States
            Self::UsAlabama => "US-AL",
            Self::UsAlaska => "US-AK",
            Self::UsArizona => "US-AZ",
            Self::UsArkansas => "US-AR",
            Self::UsCalifornia => "US-CA",
            Self::UsColorado => "US-CO",
            Self::UsConnecticut => "US-CT",
            Self::UsDelaware => "US-DE",
            Self::UsDistrictOfColumbia => "US-DC",
            Self::UsFlorida => "US-FL",
            Self::UsGeorgia => "US-GA",
            Self::UsHawaii => "US-HI",
            Self::UsIdaho => "US-ID",
            Self::UsIllinois => "US-IL",
            Self::UsIndiana => "US-IN",
            Self::UsIowa => "US-IA",
            Self::UsKansas => "US-KS",
            Self::UsKentucky => "US-KY",
            Self::UsLouisiana => "US-LA",
            Self::UsMaine => "US-ME",
            Self::UsMaryland => "US-MD",
            Self::UsMassachusetts => "US-MA",
            Self::UsMichigan => "US-MI",
            Self::UsMinnesota => "US-MN",
            Self::UsMississippi => "US-MS",
            Self::UsMissouri => "US-MO",
            Self::UsMontana => "US-MT",
            Self::UsNebraska => "US-NE",
            Self::UsNevada => "US-NV",
            Self::UsNewHampshire => "US-NH",
            Self::UsNewJersey => "US-NJ",
            Self::UsNewMexico => "US-NM",
            Self::UsNewYork => "US-NY",
            Self::UsNorthCarolina => "US-NC",
            Self::UsNorthDakota => "US-ND",
            Self::UsOhio => "US-OH",
            Self::UsOklahoma => "US-OK",
            Self::UsOregon => "US-OR",
            Self::UsPennsylvania => "US-PA",
            Self::UsRhodeIsland => "US-RI",
            Self::UsSouthCarolina => "US-SC",
            Self::UsSouthDakota => "US-SD",
            Self::UsTennessee => "US-TN",
            Self::UsTexas => "US-TX",
            Self::UsUtah => "US-UT",
            Self::UsVermont => "US-VT",
            Self::UsVirginia => "US-VA",
            Self::UsWashington => "US-WA",
            Self::UsWestVirginia => "US-WV",
            Self::UsWisconsin => "US-WI",
            Self::UsWyoming => "US-WY",
            // Canada
            Self::CaAlberta => "CA-AB",
            Self::CaBritishColumbia => "CA-BC",
            Self::CaManitoba => "CA-MB",
            Self::CaNewBrunswick => "CA-NB",
            Self::CaNewfoundland => "CA-NL",
            Self::CaNorthwestTerritories => "CA-NT",
            Self::CaNovaScotia => "CA-NS",
            Self::CaNunavut => "CA-NU",
            Self::CaOntario => "CA-ON",
            Self::CaPrinceEdwardIsland => "CA-PE",
            Self::CaQuebec => "CA-QC",
            Self::CaSaskatchewan => "CA-SK",
            Self::CaYukon => "CA-YT",
            // Austrailia
            Self::AuWesternAustrailia => "AU-WA",
            Self::AuVictoria => "AU-VIC",
            Self::AuSouthAustrailia => "AU-SA",
            Self::AuNorthernTerritory => "AU-NT",
            Self::AuQueensland => "AU-QLD",
            Self::AuTasmania => "AU-TAS",
            Self::AuNewSouthWales => "AU-NSW",
            Self::AuAustralianCapitalTerritory => "AU-ACT",
            // Netherlands
            Self::NlCountrywide => "NL-ALL",
            Self::NlDrenthe => "NL-DR",
            Self::NlFlevoland => "NL-FL",
            Self::NlFriesland => "NL-FR",
            Self::NlGelderland => "NL-GE",
            Self::NlGroningen => "NL-GR",
            Self::NlLimburg => "NL-LI",
            Self::NlNoordBrabant => "NL-NB",
            Self::NlNoordHolland => "NL-NH",
            Self::NlOverijssel => "NL-OV",
            Self::NlUtrecht => "NL-UT",
            Self::NlZeeland => "NL-ZE",
            Self::NlZuidHolland => "NL-ZH",
            // Malaysia
            Self::MyCountrywide => "MY-ALL",
            // Luxembourg
            Self::LuCountrywide => "LU-ALL",
            // Brazil
            Self::BrCountrywide => "BR-ALL",
            // Chile
            Self::ClValparaiso => "CL-VL",
            Self::ClBiobio => "CL-BI",
            Self::ClAraucania => "CL-AR",
            Self::ClSantiago => "CL-RM",
        }
    }
}

impl<'de> Deserialize<'de> for Location {
    fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct LocationVisitor;

        impl<'de> Visitor<'de> for LocationVisitor {
            type Value = Location;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                let country_list = Location::LISTED_COUNTRIES.join("\n\t");
                formatter.write_str(&format!("a state / province / territory located in one of the following countries:\n\t{}\n", country_list))
            }

            fn visit_str<E>(self, value: &str) -> result::Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                use serde::de::{self, Unexpected};

                Location::from_str(value)
                    .map_err(|_| de::Error::invalid_value(Unexpected::Str(value), &self))
            }
        }

        deserializer.deserialize_str(LocationVisitor)
    }
}
<commit_msg>only cache ureq requests for the duration of an update<commit_after>pub mod stats;

mod scrape;

use crate::config::Config;
use crate::err::{self, Result};
use notify_rust::Notification;
use serde::de::Visitor;
use serde::{Deserialize, Deserializer};
use snafu::ResultExt;
use stats::ListenerStats;
use std::borrow::Cow;
use std::cmp::{self, Eq, Ord};
use std::fmt;
use std::result;
use std::str::FromStr;
use strum_macros::EnumString;

#[derive(Debug)]
pub struct Feed<'a> {
    pub id: u32,
    pub name: String,
    pub listeners: u32,
    pub location: Location,
    pub county: Cow<'a, str>,
    pub alert: Option<String>,
}

impl<'a> Feed<'a> {
    pub fn scrape_all(config: &Config) -> Result<Vec<Self>> {
        let agent = ureq::agent();

        let mut feeds = Self::scrape_source(Source::Top50, config.misc.minimum_listeners, &agent)?;

        if let Some(loc) = config.misc.location {
            let loc_feeds =
                Self::scrape_source(Source::Location(loc), config.misc.minimum_listeners, &agent)?;

            feeds.extend(loc_feeds);
        }

        feeds.sort_unstable();
        feeds.dedup();

        Ok(feeds)
    }

    fn scrape_source(source: Source, min_listeners: u32, agent: &ureq::Agent) -> Result<Vec<Self>> {
        let resp = agent
            .get(source.url().as_ref())
            .timeout_connect(15_000)
            .timeout_read(15_000)
            .call();

        if let Some(err) = resp.synthetic_error() {
            return Err(err.into());
        }

        let body = resp.into_string().context(err::HttpIO)?;

        match source {
            Source::Top50 => scrape::scrape_top(&body, min_listeners).context(err::ParseTopFeeds),
            Source::Location(location) => scrape::scrape_location(&body, min_listeners, location)
                .context(err::ParseLocationFeeds { location }),
        }
    }
}

impl<'a> PartialEq for Feed<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl<'a> Eq for Feed<'a> {}

impl<'a> PartialOrd for Feed<'a> {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<'a> Ord for Feed<'a> {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.id.cmp(&other.id)
    }
}

pub enum Source {
    Top50,
    Location(Location),
}

impl Source {
    pub fn url(&self) -> Cow<str> {
        match self {
            Self::Top50 => "https://www.broadcastify.com/listen/top".into(),
            Self::Location(loc) => {
                format!("https://www.broadcastify.com/listen/stid/{}", loc.id()).into()
            }
        }
    }
}

#[derive(Debug)]
pub struct FeedNotif<'a> {
    pub feed: Feed<'a>,
    pub jump: f32,
}

impl<'a> FeedNotif<'a> {
    pub fn new(feed: Feed<'a>, stats: &ListenerStats) -> Self {
        Self {
            feed,
            jump: stats.jump,
        }
    }

    pub fn show_notif(&self, index: u32, max_index: u32) -> Result<()> {
        let title = format!(
            concat!(env!("CARGO_PKG_NAME"), " update {} of {}"),
            index, max_index
        );

        let alert = match &self.feed.alert {
            Some(alert) => Cow::Owned(format!("\nalert: {}", alert)),
            None => Cow::Borrowed(""),
        };

        let body = format!(
            "{abbrev} | {name}\n{listeners} (^{jump}){alert}",
            abbrev = self.feed.location.abbrev(),
            name = self.feed.name,
            listeners = self.feed.listeners,
            jump = self.jump as i32,
            alert = alert,
        );

        Notification::new()
            .summary(&title)
            .body(&body)
            .show()
            .context(err::CreateNotif)
            .map(|_| ())
    }

    pub fn sort_all(notifs: &mut [Self], config: &Config) {
        use crate::config::{SortOrder, SortType};

        notifs.sort_unstable_by(|x, y| {
            let (x, y) = match config.sorting.order {
                SortOrder::Ascending => (x, y),
                SortOrder::Descending => (y, x),
            };

            match config.sorting.value {
                SortType::Listeners => x.feed.listeners.cmp(&y.feed.listeners),
                SortType::Jump => {
                    let x_jump = x.jump as i32;
                    let y_jump = y.jump as i32;

                    x_jump.cmp(&y_jump)
                }
            }
        });
    }

    pub fn show_all(notifs: &[Self]) -> Result<()> {
        let num_notifs = notifs.len() as u32;

        for (i, notif) in notifs.iter().enumerate() {
            notif.show_notif(1 + i as u32, num_notifs)?;
        }

        Ok(())
    }
}

/// List of all states / provinces / territories on Broadcastify that have a significant feed presence or airport feeds.
/// Every location can be mapped to its state ID as it appears on Broadcastify.
///
/// Airport feeds are used as a factor for being on this list because there's a chance of a major event occuring on them, at least more so than just amateur radio stations.
#[derive(Copy, Clone, Debug, EnumString, Eq, FromPrimitive, Hash, PartialEq)]
#[strum(serialize_all = "kebab_case")]
#[repr(u32)]
pub enum Location {
    // United States
    UsAlabama = 1,
    UsAlaska,
    UsArizona = 4,
    UsArkansas,
    UsCalifornia,
    UsColorado = 8,
    UsConnecticut,
    UsDelaware,
    UsDistrictOfColumbia,
    UsFlorida,
    UsGeorgia,
    UsHawaii = 15,
    UsIdaho,
    UsIllinois,
    UsIndiana,
    UsIowa,
    UsKansas,
    UsKentucky,
    UsLouisiana,
    UsMaine,
    UsMaryland,
    UsMassachusetts,
    UsMichigan,
    UsMinnesota,
    UsMississippi,
    UsMissouri,
    UsMontana,
    UsNebraska,
    UsNevada,
    UsNewHampshire,
    UsNewJersey,
    UsNewMexico,
    UsNewYork,
    UsNorthCarolina,
    UsNorthDakota,
    UsOhio,
    UsOklahoma,
    UsOregon,
    UsPennsylvania,
    UsRhodeIsland = 44,
    UsSouthCarolina,
    UsSouthDakota,
    UsTennessee,
    UsTexas,
    UsUtah,
    UsVermont,
    UsVirginia,
    UsWashington = 53,
    UsWestVirginia,
    UsWisconsin,
    UsWyoming,
    // Canada
    CaAlberta = 101,
    CaBritishColumbia,
    CaManitoba,
    CaNewBrunswick,
    CaNewfoundland,
    CaNorthwestTerritories,
    CaNovaScotia,
    CaNunavut,
    CaOntario,
    CaPrinceEdwardIsland,
    CaQuebec,
    CaSaskatchewan,
    CaYukon,
    // Austrailia
    AuWesternAustrailia = 151,
    AuVictoria,
    AuSouthAustrailia,
    AuNorthernTerritory,
    AuQueensland,
    AuTasmania,
    AuNewSouthWales,
    AuAustralianCapitalTerritory,
    // Netherlands
    NlCountrywide = 223,
    NlDrenthe = 688,
    NlFlevoland,
    NlFriesland,
    NlGelderland,
    NlGroningen,
    NlLimburg,
    NlNoordBrabant,
    NlNoordHolland,
    NlOverijssel,
    NlUtrecht,
    NlZeeland,
    NlZuidHolland,
    // Malaysia
    MyCountrywide = 231,
    // Luxembourg
    LuCountrywide = 252,
    // Brazil
    BrCountrywide = 345,
    // Chile
    ClValparaiso = 714,
    ClBiobio = 717,
    ClAraucania,
    ClSantiago = 723,
}

impl Location {
    const LISTED_COUNTRIES: [&'static str; 8] = [
        "Austrailia",
        "Brazil",
        "Canada",
        "Chile",
        "Luxembourg",
        "Malaysia",
        "Netherlands",
        "United States",
    ];

    #[inline(always)]
    pub fn id(self) -> u32 {
        self as u32
    }

    pub fn abbrev(self) -> &'static str {
        match self {
            // United States
            Self::UsAlabama => "US-AL",
            Self::UsAlaska => "US-AK",
            Self::UsArizona => "US-AZ",
            Self::UsArkansas => "US-AR",
            Self::UsCalifornia => "US-CA",
            Self::UsColorado => "US-CO",
            Self::UsConnecticut => "US-CT",
            Self::UsDelaware => "US-DE",
            Self::UsDistrictOfColumbia => "US-DC",
            Self::UsFlorida => "US-FL",
            Self::UsGeorgia => "US-GA",
            Self::UsHawaii => "US-HI",
            Self::UsIdaho => "US-ID",
            Self::UsIllinois => "US-IL",
            Self::UsIndiana => "US-IN",
            Self::UsIowa => "US-IA",
            Self::UsKansas => "US-KS",
            Self::UsKentucky => "US-KY",
            Self::UsLouisiana => "US-LA",
            Self::UsMaine => "US-ME",
            Self::UsMaryland => "US-MD",
            Self::UsMassachusetts => "US-MA",
            Self::UsMichigan => "US-MI",
            Self::UsMinnesota => "US-MN",
            Self::UsMississippi => "US-MS",
            Self::UsMissouri => "US-MO",
            Self::UsMontana => "US-MT",
            Self::UsNebraska => "US-NE",
            Self::UsNevada => "US-NV",
            Self::UsNewHampshire => "US-NH",
            Self::UsNewJersey => "US-NJ",
            Self::UsNewMexico => "US-NM",
            Self::UsNewYork => "US-NY",
            Self::UsNorthCarolina => "US-NC",
            Self::UsNorthDakota => "US-ND",
            Self::UsOhio => "US-OH",
            Self::UsOklahoma => "US-OK",
            Self::UsOregon => "US-OR",
            Self::UsPennsylvania => "US-PA",
            Self::UsRhodeIsland => "US-RI",
            Self::UsSouthCarolina => "US-SC",
            Self::UsSouthDakota => "US-SD",
            Self::UsTennessee => "US-TN",
            Self::UsTexas => "US-TX",
            Self::UsUtah => "US-UT",
            Self::UsVermont => "US-VT",
            Self::UsVirginia => "US-VA",
            Self::UsWashington => "US-WA",
            Self::UsWestVirginia => "US-WV",
            Self::UsWisconsin => "US-WI",
            Self::UsWyoming => "US-WY",
            // Canada
            Self::CaAlberta => "CA-AB",
            Self::CaBritishColumbia => "CA-BC",
            Self::CaManitoba => "CA-MB",
            Self::CaNewBrunswick => "CA-NB",
            Self::CaNewfoundland => "CA-NL",
            Self::CaNorthwestTerritories => "CA-NT",
            Self::CaNovaScotia => "CA-NS",
            Self::CaNunavut => "CA-NU",
            Self::CaOntario => "CA-ON",
            Self::CaPrinceEdwardIsland => "CA-PE",
            Self::CaQuebec => "CA-QC",
            Self::CaSaskatchewan => "CA-SK",
            Self::CaYukon => "CA-YT",
            // Austrailia
            Self::AuWesternAustrailia => "AU-WA",
            Self::AuVictoria => "AU-VIC",
            Self::AuSouthAustrailia => "AU-SA",
            Self::AuNorthernTerritory => "AU-NT",
            Self::AuQueensland => "AU-QLD",
            Self::AuTasmania => "AU-TAS",
            Self::AuNewSouthWales => "AU-NSW",
            Self::AuAustralianCapitalTerritory => "AU-ACT",
            // Netherlands
            Self::NlCountrywide => "NL-ALL",
            Self::NlDrenthe => "NL-DR",
            Self::NlFlevoland => "NL-FL",
            Self::NlFriesland => "NL-FR",
            Self::NlGelderland => "NL-GE",
            Self::NlGroningen => "NL-GR",
            Self::NlLimburg => "NL-LI",
            Self::NlNoordBrabant => "NL-NB",
            Self::NlNoordHolland => "NL-NH",
            Self::NlOverijssel => "NL-OV",
            Self::NlUtrecht => "NL-UT",
            Self::NlZeeland => "NL-ZE",
            Self::NlZuidHolland => "NL-ZH",
            // Malaysia
            Self::MyCountrywide => "MY-ALL",
            // Luxembourg
            Self::LuCountrywide => "LU-ALL",
            // Brazil
            Self::BrCountrywide => "BR-ALL",
            // Chile
            Self::ClValparaiso => "CL-VL",
            Self::ClBiobio => "CL-BI",
            Self::ClAraucania => "CL-AR",
            Self::ClSantiago => "CL-RM",
        }
    }
}

impl<'de> Deserialize<'de> for Location {
    fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct LocationVisitor;

        impl<'de> Visitor<'de> for LocationVisitor {
            type Value = Location;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                let country_list = Location::LISTED_COUNTRIES.join("\n\t");
                formatter.write_str(&format!("a state / province / territory located in one of the following countries:\n\t{}\n", country_list))
            }

            fn visit_str<E>(self, value: &str) -> result::Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                use serde::de::{self, Unexpected};

                Location::from_str(value)
                    .map_err(|_| de::Error::invalid_value(Unexpected::Str(value), &self))
            }
        }

        deserializer.deserialize_str(LocationVisitor)
    }
}
<|endoftext|>


<commit_before>use std::sync::{Once, ONCE_INIT};
use std::c_vec::CVec;
use std::{io,mem};
use std::collections::HashMap;
use libc::{c_void,c_int,c_long,c_double,size_t};
use super::{consts,err,info,opt};
use super::err::ErrCode;
use http::body::Body;
use http::{header,Response};

type CURL = c_void;
pub type ProgressCb<'a> = |uint, uint, uint, uint|:'a -> ();

#[link(name = "curl")]
extern {
    pub fn curl_easy_init() -> *mut CURL;
    pub fn curl_easy_setopt(curl: *mut CURL, option: opt::Opt, ...) -> ErrCode;
    pub fn curl_easy_perform(curl: *mut CURL) -> ErrCode;
    pub fn curl_easy_cleanup(curl: *mut CURL);
    pub fn curl_easy_getinfo(curl: *const CURL, info: info::Key, ...) -> ErrCode;
    pub fn curl_global_cleanup();
}

pub struct Easy {
    curl: *mut CURL
}

impl Easy {
    pub fn new() -> Easy {
        // Ensure that curl is globally initialized
        global_init();

        let handle = unsafe {
            let p = curl_easy_init();
            curl_easy_setopt(p, opt::NOPROGRESS, 0u);
            p
        };

        Easy { curl: handle }
    }

    #[inline]
    pub fn setopt<T: opt::OptVal>(&mut self, option: opt::Opt, val: T) -> Result<(), err::ErrCode> {
        // TODO: Prevent setting callback related options
        let mut res = err::OK;

        unsafe {
            val.with_c_repr(|repr| {
                res = curl_easy_setopt(self.curl, option, repr);
            })
        }

        if res.is_success() { Ok(()) } else { Err(res) }
    }

    #[inline]
    pub fn perform(&mut self, body: Option<&mut Body>, progress: Option<ProgressCb>) -> Result<Response, err::ErrCode> {
        let mut builder = ResponseBuilder::new();

        unsafe {
            let resp_p: uint = mem::transmute(&builder);
            let body_p: uint = match body {
                Some(b) => mem::transmute(b),
                None => 0
            };

            let progress_p: uint = match progress.as_ref() {
                Some(cb) => mem::transmute(cb),
                None => 0
            };

            debug!("setting read fn: {}", body_p != 0);

            // Set callback options
            curl_easy_setopt(self.curl, opt::READFUNCTION, curl_read_fn);
            curl_easy_setopt(self.curl, opt::READDATA, body_p);

            curl_easy_setopt(self.curl, opt::WRITEFUNCTION, curl_write_fn);
            curl_easy_setopt(self.curl, opt::WRITEDATA, resp_p);

            curl_easy_setopt(self.curl, opt::HEADERFUNCTION, curl_header_fn);
            curl_easy_setopt(self.curl, opt::HEADERDATA, resp_p);

            curl_easy_setopt(self.curl, opt::PROGRESSFUNCTION, curl_progress_fn);
            curl_easy_setopt(self.curl, opt::PROGRESSDATA, progress_p);
        }

        let err = unsafe { curl_easy_perform(self.curl) };

        // If the request failed, abort here
        if !err.is_success() {
            return Err(err);
        }

        // Try to get the response code
        builder.code = try!(self.get_response_code());

        Ok(builder.build())
    }

    pub fn get_response_code(&self) -> Result<uint, err::ErrCode> {
        Ok(try!(self.get_info_long(info::RESPONSE_CODE)) as uint)
    }

    pub fn get_total_time(&self) -> Result<uint, err::ErrCode> {
        Ok(try!(self.get_info_long(info::TOTAL_TIME)) as uint)
    }

    fn get_info_long(&self, key: info::Key) -> Result<c_long, err::ErrCode> {
        let v: c_long = 0;
        let res = unsafe {
            curl_easy_getinfo(self.curl as *const CURL, key, &v)
        };

        if !res.is_success() {
            return Err(res);
        }

        Ok(v)
    }
}

#[inline]
fn global_init() {
    // Schedule curl to be cleaned up after we're done with this whole process
    static mut INIT: Once = ONCE_INIT;
    unsafe {
        INIT.doit(|| ::std::rt::at_exit(proc() curl_global_cleanup()))
    }
}

impl Drop for Easy {
    fn drop(&mut self) {
        unsafe { curl_easy_cleanup(self.curl) }
    }
}

/*
 *
 * TODO: Move this into handle
 *
 */

struct ResponseBuilder {
    code: uint,
    hdrs: HashMap<String,Vec<String>>,
    body: Vec<u8>
}

impl ResponseBuilder {
    fn new() -> ResponseBuilder {
        ResponseBuilder {
            code: 0,
            hdrs: HashMap::new(),
            body: Vec::new()
        }
    }

    fn add_header(&mut self, name: &str, val: &str) {
        // TODO: Reduce allocations
        use std::ascii::OwnedStrAsciiExt;
        let name = name.to_string().into_ascii_lower();

        let inserted = match self.hdrs.find_mut(&name) {
            Some(vals) => {
                vals.push(val.to_string());
                true
            }
            None => false
        };

        if !inserted {
            self.hdrs.insert(name, vec!(val.to_string()));
        }
    }

    fn build(self) -> Response {
        let ResponseBuilder { code, hdrs, body } = self;
        Response::new(code, hdrs, body)
    }
}

/*
 *
 * ===== Callbacks =====
 */

pub extern "C" fn curl_read_fn(p: *mut u8, size: size_t, nmemb: size_t, body: *mut Body) -> size_t {
    if body.is_null() {
        return 0;
    }

    let mut dst = unsafe { CVec::new(p, (size * nmemb) as uint) };
    let body: &mut Body = unsafe { mem::transmute(body) };

    match body.read(dst.as_mut_slice()) {
        Ok(len) => len as size_t,
        Err(e) => {
            match e.kind {
                io::EndOfFile => 0 as size_t,
                _ => consts::CURL_READFUNC_ABORT as size_t
            }
        }
    }
}

pub extern "C" fn curl_write_fn(p: *mut u8, size: size_t, nmemb: size_t, resp: *mut ResponseBuilder) -> size_t {
    if !resp.is_null() {
        let builder: &mut ResponseBuilder = unsafe { mem::transmute(resp) };
        let chunk = unsafe { CVec::new(p, (size * nmemb) as uint) };
        builder.body.push_all(chunk.as_slice());
    }

    size * nmemb
}

pub extern "C" fn curl_header_fn(p: *mut u8, size: size_t, nmemb: size_t, resp: &mut ResponseBuilder) -> size_t {
    // TODO: Skip the first call (it seems to be the status line)

    let vec = unsafe { CVec::new(p, (size * nmemb) as uint) };

    match header::parse(vec.as_slice()) {
        Some((name, val)) => {
            resp.add_header(name, val);
        }
        None => {}
    }

    vec.len() as size_t
}

pub extern "C" fn curl_progress_fn(cb: *mut ProgressCb, dltotal: c_double, dlnow: c_double, ultotal: c_double, ulnow: c_double) -> c_int {
    #[inline]
    fn to_uint(v: c_double) -> uint {
        if v > 0.0 { v as uint } else { 0 }
    }

    if !cb.is_null() {
        let cb: &mut ProgressCb = unsafe { &mut *cb };
        (*cb)(to_uint(dltotal), to_uint(dlnow), to_uint(ultotal), to_uint(ulnow));
    }

    0
}
<commit_msg>Replace OwnedStrAsciiExt usage<commit_after>use std::sync::{Once, ONCE_INIT};
use std::c_vec::CVec;
use std::{io,mem};
use std::collections::HashMap;
use libc::{c_void,c_int,c_long,c_double,size_t};
use super::{consts,err,info,opt};
use super::err::ErrCode;
use http::body::Body;
use http::{header,Response};

type CURL = c_void;
pub type ProgressCb<'a> = |uint, uint, uint, uint|:'a -> ();

#[link(name = "curl")]
extern {
    pub fn curl_easy_init() -> *mut CURL;
    pub fn curl_easy_setopt(curl: *mut CURL, option: opt::Opt, ...) -> ErrCode;
    pub fn curl_easy_perform(curl: *mut CURL) -> ErrCode;
    pub fn curl_easy_cleanup(curl: *mut CURL);
    pub fn curl_easy_getinfo(curl: *const CURL, info: info::Key, ...) -> ErrCode;
    pub fn curl_global_cleanup();
}

pub struct Easy {
    curl: *mut CURL
}

impl Easy {
    pub fn new() -> Easy {
        // Ensure that curl is globally initialized
        global_init();

        let handle = unsafe {
            let p = curl_easy_init();
            curl_easy_setopt(p, opt::NOPROGRESS, 0u);
            p
        };

        Easy { curl: handle }
    }

    #[inline]
    pub fn setopt<T: opt::OptVal>(&mut self, option: opt::Opt, val: T) -> Result<(), err::ErrCode> {
        // TODO: Prevent setting callback related options
        let mut res = err::OK;

        unsafe {
            val.with_c_repr(|repr| {
                res = curl_easy_setopt(self.curl, option, repr);
            })
        }

        if res.is_success() { Ok(()) } else { Err(res) }
    }

    #[inline]
    pub fn perform(&mut self, body: Option<&mut Body>, progress: Option<ProgressCb>) -> Result<Response, err::ErrCode> {
        let mut builder = ResponseBuilder::new();

        unsafe {
            let resp_p: uint = mem::transmute(&builder);
            let body_p: uint = match body {
                Some(b) => mem::transmute(b),
                None => 0
            };

            let progress_p: uint = match progress.as_ref() {
                Some(cb) => mem::transmute(cb),
                None => 0
            };

            debug!("setting read fn: {}", body_p != 0);

            // Set callback options
            curl_easy_setopt(self.curl, opt::READFUNCTION, curl_read_fn);
            curl_easy_setopt(self.curl, opt::READDATA, body_p);

            curl_easy_setopt(self.curl, opt::WRITEFUNCTION, curl_write_fn);
            curl_easy_setopt(self.curl, opt::WRITEDATA, resp_p);

            curl_easy_setopt(self.curl, opt::HEADERFUNCTION, curl_header_fn);
            curl_easy_setopt(self.curl, opt::HEADERDATA, resp_p);

            curl_easy_setopt(self.curl, opt::PROGRESSFUNCTION, curl_progress_fn);
            curl_easy_setopt(self.curl, opt::PROGRESSDATA, progress_p);
        }

        let err = unsafe { curl_easy_perform(self.curl) };

        // If the request failed, abort here
        if !err.is_success() {
            return Err(err);
        }

        // Try to get the response code
        builder.code = try!(self.get_response_code());

        Ok(builder.build())
    }

    pub fn get_response_code(&self) -> Result<uint, err::ErrCode> {
        Ok(try!(self.get_info_long(info::RESPONSE_CODE)) as uint)
    }

    pub fn get_total_time(&self) -> Result<uint, err::ErrCode> {
        Ok(try!(self.get_info_long(info::TOTAL_TIME)) as uint)
    }

    fn get_info_long(&self, key: info::Key) -> Result<c_long, err::ErrCode> {
        let v: c_long = 0;
        let res = unsafe {
            curl_easy_getinfo(self.curl as *const CURL, key, &v)
        };

        if !res.is_success() {
            return Err(res);
        }

        Ok(v)
    }
}

#[inline]
fn global_init() {
    // Schedule curl to be cleaned up after we're done with this whole process
    static mut INIT: Once = ONCE_INIT;
    unsafe {
        INIT.doit(|| ::std::rt::at_exit(proc() curl_global_cleanup()))
    }
}

impl Drop for Easy {
    fn drop(&mut self) {
        unsafe { curl_easy_cleanup(self.curl) }
    }
}

/*
 *
 * TODO: Move this into handle
 *
 */

struct ResponseBuilder {
    code: uint,
    hdrs: HashMap<String,Vec<String>>,
    body: Vec<u8>
}

impl ResponseBuilder {
    fn new() -> ResponseBuilder {
        ResponseBuilder {
            code: 0,
            hdrs: HashMap::new(),
            body: Vec::new()
        }
    }

    fn add_header(&mut self, name: &str, val: &str) {
        // TODO: Reduce allocations
        use std::ascii::OwnedAsciiExt;
        let name = name.to_string().into_ascii_lower();

        let inserted = match self.hdrs.find_mut(&name) {
            Some(vals) => {
                vals.push(val.to_string());
                true
            }
            None => false
        };

        if !inserted {
            self.hdrs.insert(name, vec!(val.to_string()));
        }
    }

    fn build(self) -> Response {
        let ResponseBuilder { code, hdrs, body } = self;
        Response::new(code, hdrs, body)
    }
}

/*
 *
 * ===== Callbacks =====
 */

pub extern "C" fn curl_read_fn(p: *mut u8, size: size_t, nmemb: size_t, body: *mut Body) -> size_t {
    if body.is_null() {
        return 0;
    }

    let mut dst = unsafe { CVec::new(p, (size * nmemb) as uint) };
    let body: &mut Body = unsafe { mem::transmute(body) };

    match body.read(dst.as_mut_slice()) {
        Ok(len) => len as size_t,
        Err(e) => {
            match e.kind {
                io::EndOfFile => 0 as size_t,
                _ => consts::CURL_READFUNC_ABORT as size_t
            }
        }
    }
}

pub extern "C" fn curl_write_fn(p: *mut u8, size: size_t, nmemb: size_t, resp: *mut ResponseBuilder) -> size_t {
    if !resp.is_null() {
        let builder: &mut ResponseBuilder = unsafe { mem::transmute(resp) };
        let chunk = unsafe { CVec::new(p, (size * nmemb) as uint) };
        builder.body.push_all(chunk.as_slice());
    }

    size * nmemb
}

pub extern "C" fn curl_header_fn(p: *mut u8, size: size_t, nmemb: size_t, resp: &mut ResponseBuilder) -> size_t {
    // TODO: Skip the first call (it seems to be the status line)

    let vec = unsafe { CVec::new(p, (size * nmemb) as uint) };

    match header::parse(vec.as_slice()) {
        Some((name, val)) => {
            resp.add_header(name, val);
        }
        None => {}
    }

    vec.len() as size_t
}

pub extern "C" fn curl_progress_fn(cb: *mut ProgressCb, dltotal: c_double, dlnow: c_double, ultotal: c_double, ulnow: c_double) -> c_int {
    #[inline]
    fn to_uint(v: c_double) -> uint {
        if v > 0.0 { v as uint } else { 0 }
    }

    if !cb.is_null() {
        let cb: &mut ProgressCb = unsafe { &mut *cb };
        (*cb)(to_uint(dltotal), to_uint(dlnow), to_uint(ultotal), to_uint(ulnow));
    }

    0
}
<|endoftext|>


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

#[cfg(feature = "glfw-sys")]
#[link(name = "glfw3", kind = "static")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
// leaving off `kind = static` allows for the specification of a dynamic library if desired
#[link(name = "glfw3")]
extern {}

#[cfg(target_os="windows")]
#[link(name = "opengl32")]
#[link(name = "gdi32")]
extern {}

#[cfg(any(target_os="linux", target_os="freebsd", target_os="dragonfly"))]
#[link(name = "X11")]
#[link(name = "GL")]
#[link(name = "Xxf86vm")]
#[link(name = "Xrandr")]
#[link(name = "Xi")]
#[link(name = "Xcursor")]
#[link(name = "Xinerama")]
extern {}

#[cfg(target_os="macos")]
#[link(name = "Cocoa", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "IOKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
extern {}
<commit_msg>Fix #292<commit_after>// Copyright 2014 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(feature = "glfw-sys")]
#[link(name = "glfw3", kind = "static")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
// leaving off `kind = static` allows for the specification of a dynamic library if desired
#[cfg(target_family = "unix")]
#[link(name = "glfw")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
#[cfg(target_family = "windows")]
#[link(name = "glfw3")]
extern {}

#[cfg(target_os="windows")]
#[link(name = "opengl32")]
#[link(name = "gdi32")]
extern {}

#[cfg(any(target_os="linux", target_os="freebsd", target_os="dragonfly"))]
#[link(name = "X11")]
#[link(name = "GL")]
#[link(name = "Xxf86vm")]
#[link(name = "Xrandr")]
#[link(name = "Xi")]
#[link(name = "Xcursor")]
#[link(name = "Xinerama")]
extern {}

#[cfg(target_os="macos")]
#[link(name = "Cocoa", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "IOKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
extern {}
<|endoftext|>


<commit_before>use std::fmt;

// TODO: figure out how to use failure for error (or some other pattern)
#[derive(Debug)]
pub enum Error {
    NotAnInstruction,
    LongInstruction,
}

/// FlowMeta is metadata that describes code flow for an instruction.
///
/// The metadata is packed into a single byte, with the expectation that,
///  there may be one `FlowMeta` instance for each (code) offset in a program.
/// However, the you should not be worried about the representation, just the interface.
#[derive(Debug, Clone)]
pub struct FlowMeta(u8);

impl FlowMeta {
    // layout:
    //
    //     +-+-+-+-+-+-+-+-+
    //     |A|B|C|D|   E   |
    //     +-+-+-+-+-+-+-+-+
    //
    //  A - unused
    //  B - has xrefs to
    //  C - has xrefs from
    //  D - does fallthrough
    //  E - insn size

    pub fn zero() -> FlowMeta {
        FlowMeta(0x0)
    }

    /// Fetch the cached length of the instruction.
    ///
    /// If longer than 14 bytes, then will return `Error::LongInstruction` and you'll have to decode
    ///  the instruction yourself.
    ///
    /// Errors:
    ///   - NotAnInstruction
    ///   - LongInstruction
    pub fn get_insn_length(&self) -> Result<u8, Error> {
        let v = self.0 & 0b0000_1111;
        match v {
            0x00 => Err(Error::NotAnInstruction),
            0x0F => Err(Error::LongInstruction),
            v @ _ => Ok(v)
        }
    }

    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    ///
    /// let mut m = FlowMeta::zero();
    ///
    /// m.set_insn_length(0);
    /// assert_eq!(m.is_insn(), false);
    /// assert!(matches!(m.get_insn_length().err().unwrap(), Error::NotAnInstruction));
    ///
    /// m.set_insn_length(1);
    /// assert_eq!(m.is_insn(), true);
    /// assert_eq!(m.get_insn_length().unwrap(), 0x1);
    ///
    /// m.set_insn_length(2);
    /// assert_eq!(m.get_insn_length().unwrap(), 0x2);
    ///
    /// m.set_insn_length(0xE);
    /// assert_eq!(m.get_insn_length().unwrap(), 0xE);
    ///
    /// m.set_insn_length(0xF);
    /// assert_eq!(m.is_insn(), true);
    /// assert!(matches!(m.get_insn_length().err().unwrap(), Error::LongInstruction));
    /// ```
    pub fn set_insn_length(&mut self, length: u8) {
        let len = if length >= 0xF {
            // instrunction length too long
            0x0F
        } else if length == 0x0 {
            // not an instruction
            0x00
        } else {
            // good flow
            length & 0x0F
        };

        self.0 = (self.0 & 0b1111_0000) | len;
    }

    pub fn is_insn(&self) -> bool {
        self.0 & 0b0000_1111 != 0
    }

    /// Does the instruction fallthrough?
    pub fn does_fallthrough(&self) -> bool {
        self.0 & 0b0001_0000 > 0
    }

    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    ///
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.does_fallthrough(), false);
    ///
    /// m.set_fallthrough();
    /// assert_eq!(m.does_fallthrough(), true);
    /// ```
    pub fn set_fallthrough(&mut self) {
        self.0 = self.0 | 0b0001_0000;
    }

    /// Does the instruction have flow xrefs from it?
    /// This does not include the fallthrough flow.
    pub fn has_xrefs_from(&self) -> bool {
        self.0 & 0b0010_0000 > 0
    }

    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    ///
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.has_xrefs_from(), false);
    ///
    /// m.set_xrefs_from();
    /// assert_eq!(m.has_xrefs_from(), true);
    /// ```
    pub fn set_xrefs_from(&mut self) {
        self.0 = self.0 | 0b0010_0000;
    }

    /// Does the instruction have flow xrefs to it?
    pub fn has_xrefs_to(&self) -> bool {
        self.0 & 0b0100_0000 > 0
    }

    /// Set the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    /// ```
    pub fn set_xrefs_to(&mut self) {
        self.0 = self.0 | 0b0100_0000;
    }

    /// Unset the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    ///
    /// m.unset_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), false);
    /// ```
    pub fn unset_xrefs_to(&mut self) {
        self.0 = self.0 & 0b1011_1111
    }
}

impl fmt::Display for FlowMeta {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FlowMeta{{length: {}, fallthrough: {}, xrefs to: {}, xrefs from: {}}}",
               match self.get_insn_length() {
                   Ok(v) => format!("0x{:x}", v),
                   Err(Error::LongInstruction) => ">=0xF".to_string(),
                   Err(Error::NotAnInstruction) => "not an instruction".to_string(),
               },
               self.does_fallthrough(),
               self.has_xrefs_to(),
               self.has_xrefs_from())
    }
}
<commit_msg>flowmeta: make copy<commit_after>use std::fmt;

// TODO: figure out how to use failure for error (or some other pattern)
#[derive(Debug)]
pub enum Error {
    NotAnInstruction,
    LongInstruction,
}

/// FlowMeta is metadata that describes code flow for an instruction.
///
/// The metadata is packed into a single byte, with the expectation that,
///  there may be one `FlowMeta` instance for each (code) offset in a program.
/// However, the you should not be worried about the representation, just the interface.
#[derive(Debug, Clone, Copy)]
pub struct FlowMeta(u8);

impl FlowMeta {
    // layout:
    //
    //     +-+-+-+-+-+-+-+-+
    //     |A|B|C|D|   E   |
    //     +-+-+-+-+-+-+-+-+
    //
    //  A - unused
    //  B - has xrefs to
    //  C - has xrefs from
    //  D - does fallthrough
    //  E - insn size

    pub fn zero() -> FlowMeta {
        FlowMeta(0x0)
    }

    /// Fetch the cached length of the instruction.
    ///
    /// If longer than 14 bytes, then will return `Error::LongInstruction` and you'll have to decode
    ///  the instruction yourself.
    ///
    /// Errors:
    ///   - NotAnInstruction
    ///   - LongInstruction
    pub fn get_insn_length(&self) -> Result<u8, Error> {
        let v = self.0 & 0b0000_1111;
        match v {
            0x00 => Err(Error::NotAnInstruction),
            0x0F => Err(Error::LongInstruction),
            v @ _ => Ok(v)
        }
    }

    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    ///
    /// let mut m = FlowMeta::zero();
    ///
    /// m.set_insn_length(0);
    /// assert_eq!(m.is_insn(), false);
    /// assert!(matches!(m.get_insn_length().err().unwrap(), Error::NotAnInstruction));
    ///
    /// m.set_insn_length(1);
    /// assert_eq!(m.is_insn(), true);
    /// assert_eq!(m.get_insn_length().unwrap(), 0x1);
    ///
    /// m.set_insn_length(2);
    /// assert_eq!(m.get_insn_length().unwrap(), 0x2);
    ///
    /// m.set_insn_length(0xE);
    /// assert_eq!(m.get_insn_length().unwrap(), 0xE);
    ///
    /// m.set_insn_length(0xF);
    /// assert_eq!(m.is_insn(), true);
    /// assert!(matches!(m.get_insn_length().err().unwrap(), Error::LongInstruction));
    /// ```
    pub fn set_insn_length(&mut self, length: u8) {
        let len = if length >= 0xF {
            // instrunction length too long
            0x0F
        } else if length == 0x0 {
            // not an instruction
            0x00
        } else {
            // good flow
            length & 0x0F
        };

        self.0 = (self.0 & 0b1111_0000) | len;
    }

    pub fn is_insn(&self) -> bool {
        self.0 & 0b0000_1111 != 0
    }

    /// Does the instruction fallthrough?
    pub fn does_fallthrough(&self) -> bool {
        self.0 & 0b0001_0000 > 0
    }

    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    ///
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.does_fallthrough(), false);
    ///
    /// m.set_fallthrough();
    /// assert_eq!(m.does_fallthrough(), true);
    /// ```
    pub fn set_fallthrough(&mut self) {
        self.0 = self.0 | 0b0001_0000;
    }

    /// Does the instruction have flow xrefs from it?
    /// This does not include the fallthrough flow.
    pub fn has_xrefs_from(&self) -> bool {
        self.0 & 0b0010_0000 > 0
    }

    /// ```
    /// use matches::matches;
    /// use lancelot::flowmeta::*;
    ///
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.has_xrefs_from(), false);
    ///
    /// m.set_xrefs_from();
    /// assert_eq!(m.has_xrefs_from(), true);
    /// ```
    pub fn set_xrefs_from(&mut self) {
        self.0 = self.0 | 0b0010_0000;
    }

    /// Does the instruction have flow xrefs to it?
    pub fn has_xrefs_to(&self) -> bool {
        self.0 & 0b0100_0000 > 0
    }

    /// Set the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    /// ```
    pub fn set_xrefs_to(&mut self) {
        self.0 = self.0 | 0b0100_0000;
    }

    /// Unset the bit indicating that there are flow xrefs to this instruction.
    ///
    /// ```
    /// use lancelot::flowmeta::*;
    /// let mut m = FlowMeta::zero();
    /// assert_eq!(m.has_xrefs_to(), false);
    ///
    /// m.set_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), true);
    ///
    /// m.unset_xrefs_to();
    /// assert_eq!(m.has_xrefs_to(), false);
    /// ```
    pub fn unset_xrefs_to(&mut self) {
        self.0 = self.0 & 0b1011_1111
    }
}

impl fmt::Display for FlowMeta {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "FlowMeta{{length: {}, fallthrough: {}, xrefs to: {}, xrefs from: {}}}",
               match self.get_insn_length() {
                   Ok(v) => format!("0x{:x}", v),
                   Err(Error::LongInstruction) => ">=0xF".to_string(),
                   Err(Error::NotAnInstruction) => "not an instruction".to_string(),
               },
               self.does_fallthrough(),
               self.has_xrefs_to(),
               self.has_xrefs_from())
    }
}
<|endoftext|>


<commit_before>use enum_primitive::FromPrimitive;

use map;
use room::Room;
use platform::random_i32;
use direction::Direction::*;

use self::RoomId::*;
use self::RoomContents::*;

const DEBUG: bool = false;
const NUM_ROOMS: usize = 19;
const NUM_MONSTERS: usize = 4;
const NUM_ROOMS_WITH_TREASURE: usize = 4;
const NUM_ROOMS_WITH_TERROR: usize = 4;
const MIN_TREASURE_AMOUNT: u8 = 10;
const MAX_TREASURE_AMOUNT: u8 = 110;

// There doesn't seem to be a convenient way to get the "size" or
// "range" of an enum's possible values, so we'll make a trait for
// that here.
//
// Ideally we could populate it automatically through a macro, but
// for now we'll just implement it manually for all our enums.
trait SizedEnum {
  fn size() -> usize;
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RoomId {
  Hallway = 0,
  AudienceChamber = 1,
  GreatHall = 2,
  PrivateMeeting = 3,
  InnerHallway = 4,
  Entrance = 5,
  Kitchen = 6,
  StoreRoom = 7,
  Lift = 8,
  RearVestibule = 9,
  Exit = 10,
  Dungeon = 11,
  Guardroom = 12,
  MasterBedroom = 13,
  UpperHallway = 14,
  Treasury = 15,
  ChambermaidsBedroom = 16,
  DressingChamber = 17,
  SmallRoom = 18,
}
}

impl SizedEnum for RoomId {
  fn size() -> usize { NUM_ROOMS }
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone)]
pub enum MonsterId {
  Werewolf = 0,
  Fleshgorger = 1,
  Maldemer = 2,
  Dragon = 3,
}
}

impl SizedEnum for MonsterId {
  fn size() -> usize { NUM_MONSTERS }
}

#[derive(Debug, Copy, Clone)]
pub enum RoomContents {
  Treasure(u8),
  Terror(MonsterId),
}

pub type GameMap<'a> = map::Map<'a, RoomId, RoomContents>;

impl<'a> GameMap<'a> {
  pub fn create_rooms() -> [Room<RoomId, RoomContents>; NUM_ROOMS] {
    [Room::new(); NUM_ROOMS]
  }

  pub fn populate(&mut self) {
    self.describe_and_connect();
    self.allot_treasure();
    self.allot_terror();
    self.ensure_treasure();
  }

  fn allot<F>(&mut self, num_rooms: usize, allotter: F)
    where F: Fn() -> RoomContents
  {
    for _ in 0..num_rooms {
      loop {
        let room_id = random_enum::<RoomId>();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let contents = allotter();
            if DEBUG {
              println!("DEBUG: Placing {:?} in {:?}.", contents, room_id);
            }
            room.contents = Some(contents);
            break;
          }
        }
      }
    }
  }

  fn allot_terror(&mut self) {
    self.allot(NUM_ROOMS_WITH_TERROR, || Terror(random_enum::<MonsterId>()))
  }

  fn allot_treasure(&mut self) {
    self.allot(NUM_ROOMS_WITH_TREASURE,
               || Treasure(random_treasure_amount()))
  }

  fn ensure_treasure(&mut self) {
    for &room_id in [Treasury, PrivateMeeting].iter() {
      let amount = random_treasure_amount();
      if DEBUG {
        println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
      }
      self.room(room_id).contents = Some(Treasure(amount));
    }
  }

  fn describe_and_connect(&mut self) {
    self.room(Entrance).describe(
      "Entrance",
      "You are at the entrance to a forbidding-looking \
       stone castle. You are facing east."
    );

    self.room(Hallway).describe(
      "Hallway",
      "You are in the hallway. \
       There is a door to the south. \
       Through the windows to the north you can see a secret herb garden."
    );

    self.room(AudienceChamber).describe(
      "Audience Chamber",
      "This is the audience chamber. \
       There is a window to the west. By looking to the right \
       through it you can see the entrance to the castle. \
       Doors leave this room to the north, east, and south."
    );

    self.room(GreatHall).describe(
      "Great Hall",
      "You are in the great hall, an L-shaped room. \
       There are doors to the east and to the north. \
       In the alcove is a door to the west."
    );

    self.room(PrivateMeeting).describe(
      "Private Meeting Room",
      "This is the monarch's private meeting room. \
       There is a single exit to the south."
    );

    self.room(InnerHallway).describe(
      "Inner Hallway",
      "This inner hallway contains a door to the north, \
       and one to the west, and a circular stairwell \
       passes through the room. \
       You can see an ornamental lake through the \
       windows to the south."
    );

    self.room(Kitchen).describe(
      "Kitchen",
      "This is the castle's kitchen. Through windows in \
       the north wall you can see a secret herb garden. \
       A door leaves the kitchen to the south."
    );

    self.connect(Entrance, East, Hallway);
    self.connect(Hallway, South, AudienceChamber);
    self.connect(GreatHall, North, AudienceChamber);
    self.connect(GreatHall, West, AudienceChamber);
    self.connect(PrivateMeeting, South, InnerHallway);
    self.connect(InnerHallway, West, GreatHall);
    self.connect(InnerHallway, Up, UpperHallway);
    self.connect(Kitchen, South, StoreRoom);
  }
}

fn random_enum<T: FromPrimitive + SizedEnum>() -> T {
  loop {
    let r = random_i32(0, T::size() as i32);
    match T::from_i32(r) {
      Some(t) => { return t; },
      None => {}
    }
  }
}

fn random_treasure_amount() -> u8 {
  random_i32(MIN_TREASURE_AMOUNT as i32, MAX_TREASURE_AMOUNT as i32) as u8
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl map::MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}
<commit_msg>Add StoreRoom and RearVestibule.<commit_after>use enum_primitive::FromPrimitive;

use map;
use room::Room;
use platform::random_i32;
use direction::Direction::*;

use self::RoomId::*;
use self::RoomContents::*;

const DEBUG: bool = false;
const NUM_ROOMS: usize = 19;
const NUM_MONSTERS: usize = 4;
const NUM_ROOMS_WITH_TREASURE: usize = 4;
const NUM_ROOMS_WITH_TERROR: usize = 4;
const MIN_TREASURE_AMOUNT: u8 = 10;
const MAX_TREASURE_AMOUNT: u8 = 110;

// There doesn't seem to be a convenient way to get the "size" or
// "range" of an enum's possible values, so we'll make a trait for
// that here.
//
// Ideally we could populate it automatically through a macro, but
// for now we'll just implement it manually for all our enums.
trait SizedEnum {
  fn size() -> usize;
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum RoomId {
  Hallway = 0,
  AudienceChamber = 1,
  GreatHall = 2,
  PrivateMeeting = 3,
  InnerHallway = 4,
  Entrance = 5,
  Kitchen = 6,
  StoreRoom = 7,
  Lift = 8,
  RearVestibule = 9,
  Exit = 10,
  Dungeon = 11,
  Guardroom = 12,
  MasterBedroom = 13,
  UpperHallway = 14,
  Treasury = 15,
  ChambermaidsBedroom = 16,
  DressingChamber = 17,
  SmallRoom = 18,
}
}

impl SizedEnum for RoomId {
  fn size() -> usize { NUM_ROOMS }
}

enum_from_primitive! {
#[derive(Debug, Copy, Clone)]
pub enum MonsterId {
  Werewolf = 0,
  Fleshgorger = 1,
  Maldemer = 2,
  Dragon = 3,
}
}

impl SizedEnum for MonsterId {
  fn size() -> usize { NUM_MONSTERS }
}

#[derive(Debug, Copy, Clone)]
pub enum RoomContents {
  Treasure(u8),
  Terror(MonsterId),
}

pub type GameMap<'a> = map::Map<'a, RoomId, RoomContents>;

impl<'a> GameMap<'a> {
  pub fn create_rooms() -> [Room<RoomId, RoomContents>; NUM_ROOMS] {
    [Room::new(); NUM_ROOMS]
  }

  pub fn populate(&mut self) {
    self.describe_and_connect();
    self.allot_treasure();
    self.allot_terror();
    self.ensure_treasure();
  }

  fn allot<F>(&mut self, num_rooms: usize, allotter: F)
    where F: Fn() -> RoomContents
  {
    for _ in 0..num_rooms {
      loop {
        let room_id = random_enum::<RoomId>();
        if room_id != Entrance && room_id != Exit {
          let room = self.room(room_id);
          if room.contents.is_none() {
            let contents = allotter();
            if DEBUG {
              println!("DEBUG: Placing {:?} in {:?}.", contents, room_id);
            }
            room.contents = Some(contents);
            break;
          }
        }
      }
    }
  }

  fn allot_terror(&mut self) {
    self.allot(NUM_ROOMS_WITH_TERROR, || Terror(random_enum::<MonsterId>()))
  }

  fn allot_treasure(&mut self) {
    self.allot(NUM_ROOMS_WITH_TREASURE,
               || Treasure(random_treasure_amount()))
  }

  fn ensure_treasure(&mut self) {
    for &room_id in [Treasury, PrivateMeeting].iter() {
      let amount = random_treasure_amount();
      if DEBUG {
        println!("DEBUG: Placing ${} in {:?}.", amount, room_id);
      }
      self.room(room_id).contents = Some(Treasure(amount));
    }
  }

  fn describe_and_connect(&mut self) {
    self.room(Entrance).describe(
      "Entrance",
      "You are at the entrance to a forbidding-looking \
       stone castle. You are facing east."
    );

    self.room(Hallway).describe(
      "Hallway",
      "You are in the hallway. \
       There is a door to the south. \
       Through the windows to the north you can see a secret herb garden."
    );

    self.room(AudienceChamber).describe(
      "Audience Chamber",
      "This is the audience chamber. \
       There is a window to the west. By looking to the right \
       through it you can see the entrance to the castle. \
       Doors leave this room to the north, east, and south."
    );

    self.room(GreatHall).describe(
      "Great Hall",
      "You are in the great hall, an L-shaped room. \
       There are doors to the east and to the north. \
       In the alcove is a door to the west."
    );

    self.room(PrivateMeeting).describe(
      "Private Meeting Room",
      "This is the monarch's private meeting room. \
       There is a single exit to the south."
    );

    self.room(InnerHallway).describe(
      "Inner Hallway",
      "This inner hallway contains a door to the north, \
       and one to the west, and a circular stairwell \
       passes through the room. \
       You can see an ornamental lake through the \
       windows to the south."
    );

    self.room(Kitchen).describe(
      "Kitchen",
      "This is the castle's kitchen. Through windows in \
       the north wall you can see a secret herb garden. \
       A door leaves the kitchen to the south."
    );

    self.room(StoreRoom).describe(
      "Store Room",
      "You are in the store room, amidst spices, \
       vegetables, and vast sacks of flour and \
       other provisions. There is a door to the north \
       and one to the south."
    );

    self.room(RearVestibule).describe(
      "Rear Vestibule",
      "You are in the rear vestibule. \
       There are windows to the south from which \
       you can see the ornamental lake. \
       There is an exit to the east, and \
       one to the north."
    );

    self.connect(Entrance, East, Hallway);
    self.connect(Hallway, South, AudienceChamber);
    self.connect(GreatHall, North, AudienceChamber);
    self.connect(GreatHall, West, AudienceChamber);
    self.connect(PrivateMeeting, South, InnerHallway);
    self.connect(InnerHallway, West, GreatHall);
    self.connect(InnerHallway, Up, UpperHallway);
    self.connect(Kitchen, South, StoreRoom);
    self.connect(StoreRoom, South, RearVestibule);
    self.connect(RearVestibule, East, Exit);
  }
}

fn random_enum<T: FromPrimitive + SizedEnum>() -> T {
  loop {
    let r = random_i32(0, T::size() as i32);
    match T::from_i32(r) {
      Some(t) => { return t; },
      None => {}
    }
  }
}

fn random_treasure_amount() -> u8 {
  random_i32(MIN_TREASURE_AMOUNT as i32, MAX_TREASURE_AMOUNT as i32) as u8
}

// Ideally we'd actually just get rid of MapRoomId and add a constraint
// to Map<T> requiring that T be type-castable as usize, but I don't
// know how to do that, so...
impl map::MapRoomId for RoomId {
  fn room_id(self) -> usize { self as usize }
}
<|endoftext|>