<commit_before>extern crate extra;

use std::env::args;
use std::io::{self};
use std::io::BufRead;
use std::process::exit;

static MAN_PAGE: &str = /* @MANSTART{mtxt} */ r#"
NAME
    mtxt - a simple tool to manipulate text from standard input.

SYNOPSIS
    mtxt [-h | --help | -u | --to-uppercase-w | -l | --to-lowercase | -a | --strip-non-ascii]

DESCRIPTION
    This utility will manipulate UTF-8 encoded text from the standard input. Unicode is supported.
    The flags are composable, unless otherwise stated.

OPTIONS
    -h
    --help
        Print this manual page.

    -u
    --to-uppercase
        Convert the input to UPPERCASE. Works correctly with Unicode. Incompatible with '-l'.

    -l
    --lowercase
        Convert the input to lowercase. Works correctly with Unicode.

    -a
    --strip-non-ascii
        Strip the input for non-ASCII bytes, outputting a valid ASCII string.

EXAMPLES
    $ echo Δ | mtxt -l
    > δ
    $ echo we got deltas Δ right | mtxt -a
    > we got deltas  right
    $ echo Japanese scripts do not have case thus 山will stay unchanged | mtxt -u
    > JAPANESE SCRIPTS DO NOT HAVE CASE THUS 山WILL STAY UNCHANGED

AUTHOR
    This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be
    reported in the Gitlab repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 Ticki

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    Do you actually read the license? wat?
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

fn main() {
    let stdin = io::stdin();
    let stdin = stdin.lock();

    // These are the options.
    let mut to_uppercase = false;
    let mut to_lowercase = false;
    let mut strip_non_ascii = false;

    for arg in args().skip(1) {
        match arg.as_str() {
            "-u" | "--to-uppercase" => to_uppercase = true,
            "-l" | "--to-lowercase" => to_lowercase = true,
            "-a" | "--strip-non-ascii" => strip_non_ascii = true,
            "-h" | "--help" => {
                print!("{}", MAN_PAGE);
            },
            a => {
                eprintln!("Error: unknown argument: {}", a);
                exit(1);
            }
        }
    }

    if to_lowercase && to_uppercase {
        // Fail, since -u and -l are incompatible.
        fail("-u and -l are incompatible. Aborting.", &mut stderr);
    }

    for u8_line in stdin.lines() {
        match u8_line {
            Err(err) => {
                eprintln!("{}", err);
                exit(1);
            },
            Ok(line) => {
                for c in line.chars() {
                    if !strip_non_ascii || strip_non_ascii && c.is_ascii() {
                        if to_uppercase {
                            print!("{}", c.to_uppercase().to_string());
                        } else if to_lowercase {
                            print!("{}", c.to_lowercase().to_string());
                        } else {
                            print!("{}", c);
                        }
                    }
                }
                println!();
            }
        }
    }
}
<commit_msg>Use explicit exit(), eprintln!()<commit_after>extern crate extra;

use std::env::args;
use std::io::{self};
use std::io::BufRead;
use std::process::exit;

static MAN_PAGE: &str = /* @MANSTART{mtxt} */ r#"
NAME
    mtxt - a simple tool to manipulate text from standard input.

SYNOPSIS
    mtxt [-h | --help | -u | --to-uppercase-w | -l | --to-lowercase | -a | --strip-non-ascii]

DESCRIPTION
    This utility will manipulate UTF-8 encoded text from the standard input. Unicode is supported.
    The flags are composable, unless otherwise stated.

OPTIONS
    -h
    --help
        Print this manual page.

    -u
    --to-uppercase
        Convert the input to UPPERCASE. Works correctly with Unicode. Incompatible with '-l'.

    -l
    --lowercase
        Convert the input to lowercase. Works correctly with Unicode.

    -a
    --strip-non-ascii
        Strip the input for non-ASCII bytes, outputting a valid ASCII string.

EXAMPLES
    $ echo Δ | mtxt -l
    > δ
    $ echo we got deltas Δ right | mtxt -a
    > we got deltas  right
    $ echo Japanese scripts do not have case thus 山will stay unchanged | mtxt -u
    > JAPANESE SCRIPTS DO NOT HAVE CASE THUS 山WILL STAY UNCHANGED

AUTHOR
    This program was written by Ticki for Redox OS. Bugs, issues, or feature requests should be
    reported in the Gitlab repository, 'redox-os/extrautils'.

COPYRIGHT
    Copyright (c) 2016 Ticki

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
    and associated documentation files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or
    substantial portions of the Software.

    Do you actually read the license? wat?
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#; /* @MANEND */

fn main() {
    let stdin = io::stdin();
    let stdin = stdin.lock();

    // These are the options.
    let mut to_uppercase = false;
    let mut to_lowercase = false;
    let mut strip_non_ascii = false;

    for arg in args().skip(1) {
        match arg.as_str() {
            "-u" | "--to-uppercase" => to_uppercase = true,
            "-l" | "--to-lowercase" => to_lowercase = true,
            "-a" | "--strip-non-ascii" => strip_non_ascii = true,
            "-h" | "--help" => {
                print!("{}", MAN_PAGE);
            },
            a => {
                eprintln!("Error: unknown argument: {}", a);
                exit(1);
            }
        }
    }

    if to_lowercase && to_uppercase {
        eprintln!("-u and -l are incompatible. Aborting.");
        exit(1);
    }

    for u8_line in stdin.lines() {
        match u8_line {
            Err(err) => {
                eprintln!("{}", err);
                exit(1);
            },
            Ok(line) => {
                for c in line.chars() {
                    if !strip_non_ascii || strip_non_ascii && c.is_ascii() {
                        if to_uppercase {
                            print!("{}", c.to_uppercase().to_string());
                        } else if to_lowercase {
                            print!("{}", c.to_lowercase().to_string());
                        } else {
                            print!("{}", c);
                        }
                    }
                }
                println!();
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::rc::Rc;
use data::{Value, List, Scope, Function, RuntimeError};
use data::RuntimeError::*;
use scope::{GlobalScope, LocalScope};
use lambda::Lambda;

pub struct BuiltinFn
{
    pub name: &'static str,
    pub do_eval: bool,
    pub func: Box<Fn(&List, &mut Scope) -> Result<Value, RuntimeError>>,
}

impl Function for BuiltinFn
{
    fn call(&self, args: &List, env: &mut Scope, do_ev: bool) -> Result<Value, RuntimeError>
    {
        if do_ev && self.do_eval
        {
            (self.func)(&try!(args.eval(env)), env)
        }
        else
        {
            (self.func)(args, env)
        }
    }
}

impl PartialEq for BuiltinFn
{
    fn eq(&self, other: &Self) -> bool
    {
        self.name == other.name
    }
}

impl fmt::Debug for BuiltinFn
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        write!(f, "BuiltinFn({})", self.name)
    }
}

macro_rules! check_arg
{
    ($it:expr, $num:expr, $cur:expr) => (match $it.next() {
        Some(val) => val,
        None => return Err(InvalidArgNum($num, $cur)),
    });
    ($it:expr, $ty:ident, $num:expr, $cur:expr) => (match $it.next() {
        Some(Value::$ty(val)) => val,
        Some(other) => return Err(InvalidArgType(stringify!($ty), other.type_name())),
        None => return Err(InvalidArgNum($num, $cur)),
    });
}

macro_rules! check_function
{
    ($it:expr, $num:expr, $cur:expr) => (match $it.next() {
        Some(Value::Builtin(func)) => func as Rc<Function>,
        Some(Value::Lambda(func)) => func as Rc<Function>,
        Some(other) => return Err(InvalidArgType("Function", other.type_name())),
        None => return Err(InvalidArgNum($num, $cur)),
    })
}

macro_rules! map_value
{
    ($v:expr, $ty:ident, $f:expr) => (match $v {
        Value::$ty(val) => Ok($f(val)),
        _ => Err(InvalidArgType(stringify!($ty), $v.type_name())),
    })
}

pub fn load_builtins(env: &mut GlobalScope)
{
    env.set_builtin("quote", false, |args, _| {
        args.iter().next().ok_or(InvalidArgNum(1, 0))
    });

    fn assign_impl<'a, F>(args: &List, env: &'a mut Scope, f: F) -> Result<Value, RuntimeError>
        where F: Fn(&'a mut Scope, &str, Value)
    {
        let mut iter = args.iter();
        let key = check_arg!(iter, Symbol, 2, 0);
        let val = try!(check_arg!(iter, 2, 1).eval(env));
        f(env, &key, val.clone());
        Ok(val)
    };

    env.set_builtin("let", false, |args, env| assign_impl(args, env, Scope::decl));
    env.set_builtin("set", false, |args, env| assign_impl(args, env, Scope::set));

    env.set_builtin("funcall", true, |args, env| args.call(env));

    env.set_builtin("apply", true, |args, env| {
        let mut iter = args.iter();
        let func = check_arg!(iter, 2, 0);
        let lst = check_arg!(iter, List, 2, 1);
        List::cons(func, lst).call(env)
    });

    env.set_builtin("map", true, |args, env| {
        let mut iter = args.iter();
        let func = check_function!(iter, 2, 0);
        let lst = check_arg!(iter, List, 2, 1);
        lst.iter().map(|val| func.call(&val.wrap(), env, false)).collect::<Result<_, _>>()
            .map(|lst| Value::List(lst))
    });

    env.set_builtin("fold", true, |args, env| {
        let mut iter = args.iter();
        let func = check_function!(iter, 3, 0);
        let init = check_arg!(iter, 3, 1);
        let lst = check_arg!(iter, List, 3, 2);
        lst.fold(init, |acc, val| func.call(&List::cons(acc, val.wrap()), env, false))
    });

    env.set_builtin("car", true, |args, _| {
        let mut iter = args.iter();
        let lst = check_arg!(iter, List, 1, 0);
        Ok(match lst {
            List::Node(cons) => cons.car.clone(),
            _ => Value::Nil,
        })
    });

    env.set_builtin("cdr", true, |args, _| {
        let mut iter = args.iter();
        let lst = check_arg!(iter, List, 1, 0);
        Ok(match lst {
            List::Node(cons) => Value::List(cons.cdr.clone()),
            _ => Value::Nil,
        })
    });

    env.set_builtin("cons", true, |args, _| {
        let mut iter = args.iter();
        let car = check_arg!(iter, 2, 0);
        let cdr = check_arg!(iter, List, 2, 1);
        Ok(Value::List(List::cons(car, cdr)))
    });

    env.set_builtin("list", true, |args, _| {
        Ok(Value::List(args.clone()))
    });

    env.set_builtin("not", true, |args, _| {
        let mut iter = args.iter();
        let val = check_arg!(iter, 1, 0);
        Ok(Value::Bool(match val {
            Value::Nil | Value::Bool(false) => true,
            _ => false,
        }))
    });

    env.set_builtin("and", false, |args, env| {
        let mut iter = args.iter();
        let mut last = Value::Bool(true);
        while let Some(val) = iter.next()
        {
            match try!(val.eval(env)) {
                v @ Value::Nil | v @ Value::Bool(false) => return Ok(v),
                other => last = other,
            }
        }
        Ok(last)
    });

    env.set_builtin("or", false, |args, env| {
        let mut iter = args.iter();
        let mut last = Value::Bool(false);
        while let Some(val) = iter.next()
        {
            match try!(val.eval(env)) {
                v @ Value::Nil | v @ Value::Bool(false) => last = v,
                other => return Ok(other),
            }
        }
        Ok(last)
    });

    env.set_builtin("if", false, |args, env| {
        let mut iter = args.iter();
        let cond = check_arg!(iter, 2, 0);
        let then = check_arg!(iter, 2, 1);
        match try!(cond.eval(env)) {
            Value::Nil | Value::Bool(false) => iter.next().unwrap_or(Value::Nil).eval(env),
            _ => then.eval(env),
        }
    });

    env.set_builtin("begin", false, |args, env| {
        let mut local = LocalScope::new(env);
        args.eval_to_value(&mut local)
    });

    env.set_builtin("eval", true, |args, env| {
        let mut iter = args.iter();
        let expr = check_arg!(iter, 1, 0);
        expr.eval(env)
    });

    env.set_builtin("lambda", false, |args, _| {
        let (arg_lst, code) = match *args {
            List::Node(ref cons) => match cons.car {
                Value::List(ref lst) => (lst, cons.cdr.clone()),
                ref other => return Err(InvalidArgType("List", other.type_name())),
            },
            List::End => return Err(InvalidArgNum(1, 0)),
        };
        arg_lst.iter().map(|val| map_value!(val, Symbol, |n: Rc<String>| (*n).clone()))
            .collect::<Result<_, _>>()
            .map(|names| Value::Lambda(Rc::new(Lambda::new(names, code))))
    });

    env.set_builtin("+", true, |args, _| {
        args.fold(0.0, |acc, val| map_value!(val, Number, |n| acc + n)).map(|n| Value::Number(n))
    });

    env.set_builtin("*", true, |args, _| {
        args.fold(1.0, |acc, val| map_value!(val, Number, |n| acc * n)).map(|n| Value::Number(n))
    });

    fn numeric_op<F, G>(args: &List, ident: f64, op: F) -> Result<Value, RuntimeError>
        where F: Fn(f64) -> G, G: Fn(f64) -> f64
    {
        match *args {
            List::Node(ref c1) => match c1.cdr {
                List::Node(_) => match c1.car {
                    Value::Number(num) => c1.cdr.fold(num, |acc, val| map_value!(val, Number, op(acc))),
                    ref other => Err(InvalidArgType("Number", other.type_name())),
                },
                List::End => map_value!(c1.car, Number, op(ident)),
            },
            List::End => Err(InvalidArgNum(1, 0)),
        }.map(|n| Value::Number(n))
    }

    env.set_builtin("-", true, |args, _| numeric_op(args, 0.0, |lhs| move |rhs| lhs - rhs));
    env.set_builtin("/", true, |args, _| numeric_op(args, 1.0, |lhs| move |rhs| lhs / rhs));

    env.set_builtin("=", true, |args, _| {
        let mut iter = args.iter();
        let va = check_arg!(iter, 2, 0);
        let vb = check_arg!(iter, 2, 1);
        Ok(Value::Bool(va == vb))
    });

    fn comp_op<F, G>(args: &List, op_num: F, op_str: G) -> Result<Value, RuntimeError>
        where F: Fn(f64, f64) -> bool, G: Fn(&str, &str) -> bool
    {
        let mut iter = args.iter();
        let va = check_arg!(iter, 2, 0);
        let vb = check_arg!(iter, 2, 1);
        match (va, vb) {
            (Value::Number(a), Value::Number(b)) => Ok(op_num(a, b)),
            (Value::String(ref a), Value::String(ref b)) => Ok(op_str(a, b)),
            (a, b) => Err(InvalidComp(a.type_name(), b.type_name())),
        }.map(|b| Value::Bool(b))
    }

    env.set_builtin("<", true, |args, _| comp_op(args, |a, b| a < b, |a, b| a < b));
    env.set_builtin(">", true, |args, _| comp_op(args, |a, b| a > b, |a, b| a > b));
    env.set_builtin("<=", true, |args, _| comp_op(args, |a, b| a <= b, |a, b| a <= b));
    env.set_builtin(">=", true, |args, _| comp_op(args, |a, b| a >= b, |a, b| a >= b));
}
<commit_msg>added builtins: atom, typeof<commit_after>use std::fmt;
use std::rc::Rc;
use data::{Value, List, Scope, Function, RuntimeError};
use data::RuntimeError::*;
use scope::{GlobalScope, LocalScope};
use lambda::Lambda;

pub struct BuiltinFn
{
    pub name: &'static str,
    pub do_eval: bool,
    pub func: Box<Fn(&List, &mut Scope) -> Result<Value, RuntimeError>>,
}

impl Function for BuiltinFn
{
    fn call(&self, args: &List, env: &mut Scope, do_ev: bool) -> Result<Value, RuntimeError>
    {
        if do_ev && self.do_eval
        {
            (self.func)(&try!(args.eval(env)), env)
        }
        else
        {
            (self.func)(args, env)
        }
    }
}

impl PartialEq for BuiltinFn
{
    fn eq(&self, other: &Self) -> bool
    {
        self.name == other.name
    }
}

impl fmt::Debug for BuiltinFn
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        write!(f, "BuiltinFn({})", self.name)
    }
}

macro_rules! check_arg
{
    ($it:expr, $num:expr, $cur:expr) => (match $it.next() {
        Some(val) => val,
        None => return Err(InvalidArgNum($num, $cur)),
    });
    ($it:expr, $ty:ident, $num:expr, $cur:expr) => (match $it.next() {
        Some(Value::$ty(val)) => val,
        Some(other) => return Err(InvalidArgType(stringify!($ty), other.type_name())),
        None => return Err(InvalidArgNum($num, $cur)),
    });
}

macro_rules! check_function
{
    ($it:expr, $num:expr, $cur:expr) => (match $it.next() {
        Some(Value::Builtin(func)) => func as Rc<Function>,
        Some(Value::Lambda(func)) => func as Rc<Function>,
        Some(other) => return Err(InvalidArgType("Function", other.type_name())),
        None => return Err(InvalidArgNum($num, $cur)),
    })
}

macro_rules! map_value
{
    ($v:expr, $ty:ident, $f:expr) => (match $v {
        Value::$ty(val) => Ok($f(val)),
        _ => Err(InvalidArgType(stringify!($ty), $v.type_name())),
    })
}

pub fn load_builtins(env: &mut GlobalScope)
{
    env.set_builtin("quote", false, |args, _| {
        args.iter().next().ok_or(InvalidArgNum(1, 0))
    });

    fn assign_impl<'a, F>(args: &List, env: &'a mut Scope, f: F) -> Result<Value, RuntimeError>
        where F: Fn(&'a mut Scope, &str, Value)
    {
        let mut iter = args.iter();
        let key = check_arg!(iter, Symbol, 2, 0);
        let val = try!(check_arg!(iter, 2, 1).eval(env));
        f(env, &key, val.clone());
        Ok(val)
    };

    env.set_builtin("let", false, |args, env| assign_impl(args, env, Scope::decl));
    env.set_builtin("set", false, |args, env| assign_impl(args, env, Scope::set));

    env.set_builtin("funcall", true, |args, env| args.call(env));

    env.set_builtin("apply", true, |args, env| {
        let mut iter = args.iter();
        let func = check_arg!(iter, 2, 0);
        let lst = check_arg!(iter, List, 2, 1);
        List::cons(func, lst).call(env)
    });

    env.set_builtin("map", true, |args, env| {
        let mut iter = args.iter();
        let func = check_function!(iter, 2, 0);
        let lst = check_arg!(iter, List, 2, 1);
        lst.iter().map(|val| func.call(&val.wrap(), env, false)).collect::<Result<_, _>>()
            .map(|lst| Value::List(lst))
    });

    env.set_builtin("fold", true, |args, env| {
        let mut iter = args.iter();
        let func = check_function!(iter, 3, 0);
        let init = check_arg!(iter, 3, 1);
        let lst = check_arg!(iter, List, 3, 2);
        lst.fold(init, |acc, val| func.call(&List::cons(acc, val.wrap()), env, false))
    });

    env.set_builtin("car", true, |args, _| {
        let mut iter = args.iter();
        let lst = check_arg!(iter, List, 1, 0);
        Ok(match lst {
            List::Node(cons) => cons.car.clone(),
            _ => Value::Nil,
        })
    });

    env.set_builtin("cdr", true, |args, _| {
        let mut iter = args.iter();
        let lst = check_arg!(iter, List, 1, 0);
        Ok(match lst {
            List::Node(cons) => Value::List(cons.cdr.clone()),
            _ => Value::Nil,
        })
    });

    env.set_builtin("cons", true, |args, _| {
        let mut iter = args.iter();
        let car = check_arg!(iter, 2, 0);
        let cdr = check_arg!(iter, List, 2, 1);
        Ok(Value::List(List::cons(car, cdr)))
    });

    env.set_builtin("list", true, |args, _| {
        Ok(Value::List(args.clone()))
    });

    env.set_builtin("not", true, |args, _| {
        let mut iter = args.iter();
        let val = check_arg!(iter, 1, 0);
        Ok(Value::Bool(match val {
            Value::Nil | Value::Bool(false) => true,
            _ => false,
        }))
    });

    env.set_builtin("and", false, |args, env| {
        let mut iter = args.iter();
        let mut last = Value::Bool(true);
        while let Some(val) = iter.next()
        {
            match try!(val.eval(env)) {
                v @ Value::Nil | v @ Value::Bool(false) => return Ok(v),
                other => last = other,
            }
        }
        Ok(last)
    });

    env.set_builtin("or", false, |args, env| {
        let mut iter = args.iter();
        let mut last = Value::Bool(false);
        while let Some(val) = iter.next()
        {
            match try!(val.eval(env)) {
                v @ Value::Nil | v @ Value::Bool(false) => last = v,
                other => return Ok(other),
            }
        }
        Ok(last)
    });

    env.set_builtin("if", false, |args, env| {
        let mut iter = args.iter();
        let cond = check_arg!(iter, 2, 0);
        let then = check_arg!(iter, 2, 1);
        match try!(cond.eval(env)) {
            Value::Nil | Value::Bool(false) => iter.next().unwrap_or(Value::Nil).eval(env),
            _ => then.eval(env),
        }
    });

    env.set_builtin("begin", false, |args, env| {
        let mut local = LocalScope::new(env);
        args.eval_to_value(&mut local)
    });

    env.set_builtin("eval", true, |args, env| {
        let mut iter = args.iter();
        let expr = check_arg!(iter, 1, 0);
        expr.eval(env)
    });

    env.set_builtin("lambda", false, |args, _| {
        let (arg_lst, code) = match *args {
            List::Node(ref cons) => match cons.car {
                Value::List(ref lst) => (lst, cons.cdr.clone()),
                ref other => return Err(InvalidArgType("List", other.type_name())),
            },
            List::End => return Err(InvalidArgNum(1, 0)),
        };
        arg_lst.iter().map(|val| map_value!(val, Symbol, |n: Rc<String>| (*n).clone()))
            .collect::<Result<_, _>>()
            .map(|names| Value::Lambda(Rc::new(Lambda::new(names, code))))
    });

    env.set_builtin("+", true, |args, _| {
        args.fold(0.0, |acc, val| map_value!(val, Number, |n| acc + n)).map(|n| Value::Number(n))
    });

    env.set_builtin("*", true, |args, _| {
        args.fold(1.0, |acc, val| map_value!(val, Number, |n| acc * n)).map(|n| Value::Number(n))
    });

    fn numeric_op<F, G>(args: &List, ident: f64, op: F) -> Result<Value, RuntimeError>
        where F: Fn(f64) -> G, G: Fn(f64) -> f64
    {
        match *args {
            List::Node(ref c1) => match c1.cdr {
                List::Node(_) => match c1.car {
                    Value::Number(num) => c1.cdr.fold(num, |acc, val| map_value!(val, Number, op(acc))),
                    ref other => Err(InvalidArgType("Number", other.type_name())),
                },
                List::End => map_value!(c1.car, Number, op(ident)),
            },
            List::End => Err(InvalidArgNum(1, 0)),
        }.map(|n| Value::Number(n))
    }

    env.set_builtin("-", true, |args, _| numeric_op(args, 0.0, |lhs| move |rhs| lhs - rhs));
    env.set_builtin("/", true, |args, _| numeric_op(args, 1.0, |lhs| move |rhs| lhs / rhs));

    env.set_builtin("=", true, |args, _| {
        let mut iter = args.iter();
        let va = check_arg!(iter, 2, 0);
        let vb = check_arg!(iter, 2, 1);
        Ok(Value::Bool(va == vb))
    });

    fn comp_op<F, G>(args: &List, op_num: F, op_str: G) -> Result<Value, RuntimeError>
        where F: Fn(f64, f64) -> bool, G: Fn(&str, &str) -> bool
    {
        let mut iter = args.iter();
        let va = check_arg!(iter, 2, 0);
        let vb = check_arg!(iter, 2, 1);
        match (va, vb) {
            (Value::Number(a), Value::Number(b)) => Ok(op_num(a, b)),
            (Value::String(ref a), Value::String(ref b)) => Ok(op_str(a, b)),
            (a, b) => Err(InvalidComp(a.type_name(), b.type_name())),
        }.map(|b| Value::Bool(b))
    }

    env.set_builtin("<", true, |args, _| comp_op(args, |a, b| a < b, |a, b| a < b));
    env.set_builtin(">", true, |args, _| comp_op(args, |a, b| a > b, |a, b| a > b));
    env.set_builtin("<=", true, |args, _| comp_op(args, |a, b| a <= b, |a, b| a <= b));
    env.set_builtin(">=", true, |args, _| comp_op(args, |a, b| a >= b, |a, b| a >= b));

    env.set_builtin("atom", true, |args, _| {
        let mut iter = args.iter();
        let val = check_arg!(iter, 1, 0);
        Ok(Value::Bool(match val {
            Value::List(List::Node(_)) => false,
            _ => true,
        }))
    });

    env.set_builtin("typeof", true, |args, _| {
        let mut iter = args.iter();
        let val = check_arg!(iter, 1, 0);
        Ok(Value::String(Rc::new(val.type_name().to_string())))
    });
}
<|endoftext|>


<commit_before>// TODO: use Impls https://github.com/iron/iron/blob/master/src/iron.rs

extern crate collections;

use collections::dlist::DList;
use collections::Deque;

static CHB_MIN_SIZE:uint = 32u;

// TODO: move to utils
fn blit<T:Clone>(src: &[T], src_ofs: uint, dst: &mut [T], dst_ofs: uint, len: uint) {
    if (src_ofs > src.len() - len) || (dst_ofs > dst.len() - len) {
        fail!("blit: invalid argument!");
    }
    let sd = dst.slice_mut(dst_ofs, dst_ofs + len);
    let ss = src.slice(src_ofs, src_ofs + len);
    let _ = sd.clone_from_slice(ss);
}

/// Chained buffer of bytes.
/// Consists of linked list of nodes.
pub struct Chain {
    head: DList<Box<Node>>,
    length: uint
}

impl Chain {
    pub fn new() -> Chain {
        return Chain{
            head: DList::new(),
            length: 0
        }
    }

    // TODO: rename len()?
    pub fn size(&self) -> uint {
        self.length
    }

    // XXX: maybe DEDUP append/prepend?
    // TODO: test: length, capacity, node size
    pub fn append_bytes(mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.back() {
            Some(nd) => {
                // Check is READONLY
                nd.room() < size
            }
            None => {
                true
            }
        };
        if should_create {
            self.create_node_tail(size);
        }
        // node could not be None here
        let node = self.head.back_mut().unwrap();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6268
        let end = node.end;
        blit(data.as_slice(), 0,
             node.dh.data.as_mut_slice(), end,
             size);
        node.end += size;
        self.length += size;
    }

    // TODO: test: length, capacity, node size
    pub fn prepend_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.front() {
            Some(nd) => {
                // Check is READONLY
                size > nd.start
            }
            None => {
                true
            }
        };
        if should_create {
            self.create_node_head(size);
        }
        // node could not be None here
        let node = self.head.front_mut().unwrap();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6268
        let start = node.start;
        blit(data.as_slice(), 0,
             node.dh.data.as_mut_slice(), start - size,
             size);
        node.start -= size;
        self.length += size;
    }

    pub fn pullup(&mut self, size: uint) -> Option<&[u8]> {
        if size == 0 || size > self.size() {
            return None
        }
        // let node = match chb.head
        None
    }

    // XXX: private

    // TODO: rename _back
    fn add_node_tail(&mut self, node: Box<Node>) {
        self.length += node.size();
        self.head.push(node);
    }

    // TODO: rename _front
    fn add_node_head(&mut self, node: Box<Node>) {
        self.length += node.size();
        self.head.push_front(node);
    }

    // TODO: rename _back
    // TODO: remove?
    fn create_node_tail(&mut self, size: uint) {
        let nsize = if size < CHB_MIN_SIZE {
            size << 1
        } else {
            size
        };
        let node = Node::new(DataHolder::new(nsize)); // Box<Node>
        self.add_node_tail(node);
    }

    // TODO: rename _front
    fn create_node_head(&mut self, size: uint) {
        let nsize = if size < CHB_MIN_SIZE {
            size << 1
        } else {
            size
        };
        let mut node = Node::new(DataHolder::new(nsize)); // Box<Node>
        let r = node.room();
        node.start = r;
        node.end = r;
        self.add_node_head(node);
    }
}

/// Node of chain buffer.
/// Owned by Chain.
struct Node {
    dh: Box<DataHolder>, // можно заменить на RC
    start: uint,
    end: uint
}

impl Node {
    fn new(dh: Box<DataHolder>) -> Box<Node> {
        let n = box Node {
            dh: dh,
            start: 0,
            end: 0
        };
        // TODO: ref dh ? auto, when using RC
        return n;
    }

    fn size(&self) -> uint {
        self.end - self.start
    }

    fn room(&self) -> uint {
        return self.dh.size - self.end;
    }
}

/// Data holder
/// TODO: can be shared among different chains
/// TODO: implement other storages: shmem, mmap
struct DataHolder{
    size: uint,
    data: Vec<u8>
}

impl DataHolder {
    fn new(size: uint) -> Box<DataHolder> {
        let dh = box DataHolder {
            size: size,
            data: Vec::from_elem(size, 0)
        };
        return dh;
    }
}

// TODO: move to test
// let mut chain = Chb::new();
// chain.append_bytes("abcdefghijklmnop".as_bytes());
// chain.prepend_bytes("xxx".as_bytes());
<commit_msg>[fix] prevent move of self<commit_after>// TODO: use Impls https://github.com/iron/iron/blob/master/src/iron.rs

extern crate collections;

use collections::dlist::DList;
use collections::Deque;

static CHB_MIN_SIZE:uint = 32u;

// TODO: move to utils
fn blit<T:Clone>(src: &[T], src_ofs: uint, dst: &mut [T], dst_ofs: uint, len: uint) {
    if (src_ofs > src.len() - len) || (dst_ofs > dst.len() - len) {
        fail!("blit: invalid argument!");
    }
    let sd = dst.slice_mut(dst_ofs, dst_ofs + len);
    let ss = src.slice(src_ofs, src_ofs + len);
    let _ = sd.clone_from_slice(ss);
}

/// Chained buffer of bytes.
/// Consists of linked list of nodes.
pub struct Chain {
    head: DList<Box<Node>>,
    length: uint
}

impl Chain {
    pub fn new() -> Chain {
        return Chain{
            head: DList::new(),
            length: 0
        }
    }

    // TODO: rename len()?
    pub fn size(&self) -> uint {
        self.length
    }

    // XXX: maybe DEDUP append/prepend?
    // TODO: test: length, capacity, node size
    pub fn append_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.back() {
            Some(nd) => {
                // Check is READONLY
                nd.room() < size
            }
            None => {
                true
            }
        };
        if should_create {
            self.create_node_tail(size);
        }
        // node could not be None here
        let node = self.head.back_mut().unwrap();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6268
        let end = node.end;
        blit(data.as_slice(), 0,
             node.dh.data.as_mut_slice(), end,
             size);
        node.end += size;
        self.length += size;
    }

    // TODO: test: length, capacity, node size
    pub fn prepend_bytes(&mut self, data: &[u8]) {
        let size = data.len();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6393
        let should_create = match self.head.front() {
            Some(nd) => {
                // Check is READONLY
                size > nd.start
            }
            None => {
                true
            }
        };
        if should_create {
            self.create_node_head(size);
        }
        // node could not be None here
        let node = self.head.front_mut().unwrap();
        // XXX: Damn, https://github.com/rust-lang/rust/issues/6268
        let start = node.start;
        blit(data.as_slice(), 0,
             node.dh.data.as_mut_slice(), start - size,
             size);
        node.start -= size;
        self.length += size;
    }

    pub fn pullup(&mut self, size: uint) -> Option<&[u8]> {
        if size == 0 || size > self.size() {
            return None
        }
        // let node = match chb.head
        None
    }

    // XXX: private

    // TODO: rename _back
    fn add_node_tail(&mut self, node: Box<Node>) {
        self.length += node.size();
        self.head.push(node);
    }

    // TODO: rename _front
    fn add_node_head(&mut self, node: Box<Node>) {
        self.length += node.size();
        self.head.push_front(node);
    }

    // TODO: rename _back
    // TODO: remove?
    fn create_node_tail(&mut self, size: uint) {
        let nsize = if size < CHB_MIN_SIZE {
            size << 1
        } else {
            size
        };
        let node = Node::new(DataHolder::new(nsize)); // Box<Node>
        self.add_node_tail(node);
    }

    // TODO: rename _front
    fn create_node_head(&mut self, size: uint) {
        let nsize = if size < CHB_MIN_SIZE {
            size << 1
        } else {
            size
        };
        let mut node = Node::new(DataHolder::new(nsize)); // Box<Node>
        let r = node.room();
        node.start = r;
        node.end = r;
        self.add_node_head(node);
    }
}

/// Node of chain buffer.
/// Owned by Chain.
struct Node {
    dh: Box<DataHolder>, // можно заменить на RC
    start: uint,
    end: uint
}

impl Node {
    fn new(dh: Box<DataHolder>) -> Box<Node> {
        let n = box Node {
            dh: dh,
            start: 0,
            end: 0
        };
        // TODO: ref dh ? auto, when using RC
        return n;
    }

    fn size(&self) -> uint {
        self.end - self.start
    }

    fn room(&self) -> uint {
        return self.dh.size - self.end;
    }
}

/// Data holder
/// TODO: can be shared among different chains
/// TODO: implement other storages: shmem, mmap
struct DataHolder{
    size: uint,
    data: Vec<u8>
}

impl DataHolder {
    fn new(size: uint) -> Box<DataHolder> {
        let dh = box DataHolder {
            size: size,
            data: Vec::from_elem(size, 0)
        };
        return dh;
    }
}

// TODO: move to test
// let mut chain = Chb::new();
// chain.append_bytes("abcdefghijklmnop".as_bytes());
// chain.prepend_bytes("xxx".as_bytes());
<|endoftext|>


<commit_before>#[macro_use]
extern crate cargotest;
extern crate hamcrest;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::prelude::*;
use std::str;

use rustc_serialize::json;

use cargotest::support::{project, execs, ProjectBuilder};
use cargotest::support::paths;
use cargotest::support::registry::{Package, cksum};
use hamcrest::assert_that;

fn setup() {
    let root = paths::root();
    t!(fs::create_dir(&root.join(".cargo")));
    t!(t!(File::create(root.join(".cargo/config"))).write_all(br#"
        [source.crates-io]
        registry = 'https://wut'
        replace-with = 'my-awesome-local-registry'

        [source.my-awesome-local-registry]
        directory = 'index'
    "#));
}

struct VendorPackage {
    p: Option<ProjectBuilder>,
    cksum: Checksum,
}

#[derive(RustcEncodable)]
struct Checksum {
    package: String,
    files: HashMap<String, String>,
}

impl VendorPackage {
    fn new(name: &str) -> VendorPackage {
        VendorPackage {
            p: Some(project(&format!("index/{}", name))),
            cksum: Checksum {
                package: String::new(),
                files: HashMap::new(),
            },
        }
    }

    fn file(&mut self, name: &str, contents: &str) -> &mut VendorPackage {
        self.p = Some(self.p.take().unwrap().file(name, contents));
        self.cksum.files.insert(name.to_string(), cksum(contents.as_bytes()));
        self
    }

    fn build(&mut self) {
        let p = self.p.take().unwrap();
        let json = json::encode(&self.cksum).unwrap();
        let p = p.file(".cargo-checksum.json", &json);
        p.build();
    }
}

#[test]
fn simple() {
    setup();

    VendorPackage::new("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .build();

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));
}

#[test]
fn not_there() {
    setup();

    project("index").build();

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(101).with_stderr("\
error: no matching package named `foo` found (required by `bar`)
location searched: [..]
version required: ^0.1.0
"));
}

#[test]
fn multiple() {
    setup();

    VendorPackage::new("foo-0.1.0")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .file(".cargo-checksum", "")
        .build();

    VendorPackage::new("foo-0.2.0")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.2.0"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .file(".cargo-checksum", "")
        .build();

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));
}

#[test]
fn crates_io_then_directory() {
    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    let cksum = Package::new("foo", "0.1.0")
                        .file("src/lib.rs", "pub fn foo() -> u32 { 0 }")
                        .publish();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[UPDATING] registry `[..]`
[DOWNLOADING] foo v0.1.0 ([..])
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));

    setup();

    let mut v = VendorPackage::new("foo");
    v.file("Cargo.toml", r#"
        [package]
        name = "foo"
        version = "0.1.0"
        authors = []
    "#);
    v.file("src/lib.rs", "pub fn foo() -> u32 { 1 }");
    v.cksum.package = cksum;
    v.build();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));
}

#[test]
fn crates_io_then_bad_checksum() {
    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", "");
    p.build();

    Package::new("foo", "0.1.0").publish();

    assert_that(p.cargo("build"),
                execs().with_status(0));
    setup();

    VendorPackage::new("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .build();

    assert_that(p.cargo("build"),
                execs().with_status(101).with_stderr("\
error: checksum for `foo v0.1.0` changed between lock files

this could be indicative of a few possible errors:

    * the lock file is corrupt
    * a replacement source in use (e.g. a mirror) returned a different checksum
    * the source itself may be corrupt in one way or another

unable to verify that `foo v0.1.0` is the same as when the lockfile was generated

"));
}

#[test]
fn bad_file_checksum() {
    setup();

    VendorPackage::new("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .build();

    let mut f = t!(File::create(paths::root().join("index/foo/src/lib.rs")));
    t!(f.write_all(b"fn foo() -> u32 { 0 }"));

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", "");
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(101).with_stderr("\
error: the listed checksum of `[..]lib.rs` has changed:
expected: [..]
actual:   [..]

directory sources are not intended to be edited, if modifications are \
required then it is recommended that [replace] is used with a forked copy of \
the source
"));
}
<commit_msg>tests/directory: Remove unnecessary dummy registry URL for crates.io<commit_after>#[macro_use]
extern crate cargotest;
extern crate hamcrest;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::prelude::*;
use std::str;

use rustc_serialize::json;

use cargotest::support::{project, execs, ProjectBuilder};
use cargotest::support::paths;
use cargotest::support::registry::{Package, cksum};
use hamcrest::assert_that;

fn setup() {
    let root = paths::root();
    t!(fs::create_dir(&root.join(".cargo")));
    t!(t!(File::create(root.join(".cargo/config"))).write_all(br#"
        [source.crates-io]
        replace-with = 'my-awesome-local-registry'

        [source.my-awesome-local-registry]
        directory = 'index'
    "#));
}

struct VendorPackage {
    p: Option<ProjectBuilder>,
    cksum: Checksum,
}

#[derive(RustcEncodable)]
struct Checksum {
    package: String,
    files: HashMap<String, String>,
}

impl VendorPackage {
    fn new(name: &str) -> VendorPackage {
        VendorPackage {
            p: Some(project(&format!("index/{}", name))),
            cksum: Checksum {
                package: String::new(),
                files: HashMap::new(),
            },
        }
    }

    fn file(&mut self, name: &str, contents: &str) -> &mut VendorPackage {
        self.p = Some(self.p.take().unwrap().file(name, contents));
        self.cksum.files.insert(name.to_string(), cksum(contents.as_bytes()));
        self
    }

    fn build(&mut self) {
        let p = self.p.take().unwrap();
        let json = json::encode(&self.cksum).unwrap();
        let p = p.file(".cargo-checksum.json", &json);
        p.build();
    }
}

#[test]
fn simple() {
    setup();

    VendorPackage::new("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .build();

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));
}

#[test]
fn not_there() {
    setup();

    project("index").build();

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(101).with_stderr("\
error: no matching package named `foo` found (required by `bar`)
location searched: [..]
version required: ^0.1.0
"));
}

#[test]
fn multiple() {
    setup();

    VendorPackage::new("foo-0.1.0")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .file(".cargo-checksum", "")
        .build();

    VendorPackage::new("foo-0.2.0")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.2.0"
            authors = []
        "#)
        .file("src/lib.rs", "pub fn foo() {}")
        .file(".cargo-checksum", "")
        .build();

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));
}

#[test]
fn crates_io_then_directory() {
    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", r#"
            extern crate foo;

            pub fn bar() {
                foo::foo();
            }
        "#);
    p.build();

    let cksum = Package::new("foo", "0.1.0")
                        .file("src/lib.rs", "pub fn foo() -> u32 { 0 }")
                        .publish();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[UPDATING] registry `[..]`
[DOWNLOADING] foo v0.1.0 ([..])
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));

    setup();

    let mut v = VendorPackage::new("foo");
    v.file("Cargo.toml", r#"
        [package]
        name = "foo"
        version = "0.1.0"
        authors = []
    "#);
    v.file("src/lib.rs", "pub fn foo() -> u32 { 1 }");
    v.cksum.package = cksum;
    v.build();

    assert_that(p.cargo("build"),
                execs().with_status(0).with_stderr("\
[COMPILING] foo v0.1.0
[COMPILING] bar v0.1.0 ([..]bar)
[FINISHED] [..]
"));
}

#[test]
fn crates_io_then_bad_checksum() {
    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", "");
    p.build();

    Package::new("foo", "0.1.0").publish();

    assert_that(p.cargo("build"),
                execs().with_status(0));
    setup();

    VendorPackage::new("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .build();

    assert_that(p.cargo("build"),
                execs().with_status(101).with_stderr("\
error: checksum for `foo v0.1.0` changed between lock files

this could be indicative of a few possible errors:

    * the lock file is corrupt
    * a replacement source in use (e.g. a mirror) returned a different checksum
    * the source itself may be corrupt in one way or another

unable to verify that `foo v0.1.0` is the same as when the lockfile was generated

"));
}

#[test]
fn bad_file_checksum() {
    setup();

    VendorPackage::new("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.1.0"
            authors = []
        "#)
        .file("src/lib.rs", "")
        .build();

    let mut f = t!(File::create(paths::root().join("index/foo/src/lib.rs")));
    t!(f.write_all(b"fn foo() -> u32 { 0 }"));

    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.1.0"
            authors = []

            [dependencies]
            foo = "0.1.0"
        "#)
        .file("src/lib.rs", "");
    p.build();

    assert_that(p.cargo("build"),
                execs().with_status(101).with_stderr("\
error: the listed checksum of `[..]lib.rs` has changed:
expected: [..]
actual:   [..]

directory sources are not intended to be edited, if modifications are \
required then it is recommended that [replace] is used with a forked copy of \
the source
"));
}
<|endoftext|>


<commit_before>#![feature(plugin, io, path, collections)]

#[plugin]
#[macro_use]
#[no_link]
extern crate rustful_macros;

extern crate rustful;

use std::old_io::{File, IoResult};
use std::sync::{Arc, RwLock};
use std::error::Error;

use rustful::{Server, Context, Response, Handler, TreeRouter};
use rustful::cache::{CachedValue, CachedProcessedFile};
use rustful::Method::Get;
use rustful::StatusCode::InternalServerError;
use rustful::header::ContentType;

fn main() {
    println!("Visit http://localhost:8080 to try this example.");

    //Cache the page
    let page = Arc::new(CachedProcessedFile::new(Path::new("examples/handler_storage/page.html"), None, read_string));

    //The shared counter state
    let value = Arc::new(RwLock::new(0));

    let router = insert_routes!{
        TreeRouter::new(): {
            "/" => Get: Counter{
                page: page.clone(),
                value: value.clone(),
                operation: None
            },
            "/add" => Get: Counter{
                page: page.clone(),
                value: value.clone(),
                operation: Some(add as fn(i32) -> i32)
            },
            "/sub" => Get: Counter{
                page: page.clone(),
                value: value.clone(),
                operation: Some(sub as fn(i32) -> i32)
            }
        }
    };

    let server_result = Server::new().port(8080).handlers(router).run();

    match server_result {
        Ok(_server) => {},
        Err(e) => println!("could not start server: {}", e.description())
    }
}

fn add(value: i32) -> i32 {
    value + 1
}

fn sub(value: i32) -> i32 {
    value - 1
}

fn read_string(mut file: IoResult<File>) -> IoResult<Option<String>> {
    //Read file into a string
    file.read_to_string().map(|s| Some(s))
}

struct Counter {
    //We are using the handler to cache the page in this exmaple
    page: Arc<CachedProcessedFile<String>>,

    value: Arc<RwLock<i32>>,
    operation: Option<fn(i32) -> i32>
}

impl Handler for Counter {
    type Cache = ();

    fn handle_request(&self, _context: Context, mut response: Response) {
        self.operation.map(|o| {
            //Lock the value for writing and update it
            let mut value = self.value.write().unwrap();
            *value = (o)(*value);
        });

        response.set_header(ContentType(content_type!("text", "html", ("charset", "UTF-8"))));

        //Insert the value into the page and write it to the response
        match *self.page.borrow() {
            Some(ref page) => {
                let count = self.value.read().unwrap().to_string();

                try_send!(response.into_writer(), page.replace("{}", &count[]));
            },
            None => {
                //Oh no! The page was not loaded!
                response.set_status(InternalServerError);
            }
        }
    }
}<commit_msg>Remove unused feature from example<commit_after>#![feature(plugin, io, path)]

#[plugin]
#[macro_use]
#[no_link]
extern crate rustful_macros;

extern crate rustful;

use std::old_io::{File, IoResult};
use std::sync::{Arc, RwLock};
use std::error::Error;

use rustful::{Server, Context, Response, Handler, TreeRouter};
use rustful::cache::{CachedValue, CachedProcessedFile};
use rustful::Method::Get;
use rustful::StatusCode::InternalServerError;
use rustful::header::ContentType;

fn main() {
    println!("Visit http://localhost:8080 to try this example.");

    //Cache the page
    let page = Arc::new(CachedProcessedFile::new(Path::new("examples/handler_storage/page.html"), None, read_string));

    //The shared counter state
    let value = Arc::new(RwLock::new(0));

    let router = insert_routes!{
        TreeRouter::new(): {
            "/" => Get: Counter{
                page: page.clone(),
                value: value.clone(),
                operation: None
            },
            "/add" => Get: Counter{
                page: page.clone(),
                value: value.clone(),
                operation: Some(add as fn(i32) -> i32)
            },
            "/sub" => Get: Counter{
                page: page.clone(),
                value: value.clone(),
                operation: Some(sub as fn(i32) -> i32)
            }
        }
    };

    let server_result = Server::new().port(8080).handlers(router).run();

    match server_result {
        Ok(_server) => {},
        Err(e) => println!("could not start server: {}", e.description())
    }
}

fn add(value: i32) -> i32 {
    value + 1
}

fn sub(value: i32) -> i32 {
    value - 1
}

fn read_string(mut file: IoResult<File>) -> IoResult<Option<String>> {
    //Read file into a string
    file.read_to_string().map(|s| Some(s))
}

struct Counter {
    //We are using the handler to cache the page in this exmaple
    page: Arc<CachedProcessedFile<String>>,

    value: Arc<RwLock<i32>>,
    operation: Option<fn(i32) -> i32>
}

impl Handler for Counter {
    type Cache = ();

    fn handle_request(&self, _context: Context, mut response: Response) {
        self.operation.map(|o| {
            //Lock the value for writing and update it
            let mut value = self.value.write().unwrap();
            *value = (o)(*value);
        });

        response.set_header(ContentType(content_type!("text", "html", ("charset", "UTF-8"))));

        //Insert the value into the page and write it to the response
        match *self.page.borrow() {
            Some(ref page) => {
                let count = self.value.read().unwrap().to_string();

                try_send!(response.into_writer(), page.replace("{}", &count[]));
            },
            None => {
                //Oh no! The page was not loaded!
                response.set_status(InternalServerError);
            }
        }
    }
}<|endoftext|>


<commit_before>//option1.rs
//Make me compile! Execute `rustlings hint option1` for hints

//I AM NOT DONE

//you can modify anything EXCEPT for this function's sig
fn print_number(maybe_number: Option<u16>) {
    println!("printing: {}", *maybe_number);
}

fn main() {
    print_number(13);
    print_number(99);

    let mut numbers: [Option<u16>; 5];
    for iter in 0..5 {
        let number_to_add: u16 = {
            ((iter * 5) + 2) / (4 * 16);
        };

        numbers[iter] = number_to_add;
    }
}<commit_msg>Update option1.rs<commit_after>//option1.rs
//Make me compile! Execute `rustlings hint option1` for hints

//I AM NOT DONE

//you can modify anything EXCEPT for this function's sig
fn print_number(maybe_number: Option<u16>) {
    println!("printing: {}", maybe_number.unwrap());
}

fn main() {
    print_number(13);
    print_number(99);

    let mut numbers: [Option<u16>; 5];
    for iter in 0..5 {
        let number_to_add: u16 = {
            ((iter * 5) + 2) / (4 * 16);
        };

        numbers[iter] = number_to_add;
    }
}
<|endoftext|>


<commit_before>//
// zhttpto.rs
//
// Reference solution for PS1
// Running on Rust 0.8
//
// Note that this code has serious security risks! You should not run it
// on any system with access to sensitive files.
//
// Special thanks to Kiet Tran for providing code we incorporated into this.
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.2

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
use std::io::println;
use std::cell::Cell;
use std::task;
use std::{os, str, io};

static PORT: int = 4414;
static IPV4_LOOPBACK: &'static str = "127.0.0.1";
static mut visitor_count: uint = 0;

fn main() {
    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: Ipv4Addr(127,0,0,1), port: PORT as u16});

    println(fmt!("Listening on tcp port %d ...", PORT));
    let mut acceptor = socket.listen().unwrap();

    // we can limit the incoming connection count.
    //for stream in acceptor.incoming().take(10 as uint) {
    for stream in acceptor.incoming() {
        println!("Saw connection!");
        let stream = Cell::new(stream);
        // Start a task to handle the connection
        do task::spawn {
            unsafe {
                visitor_count += 1;
            }
            let mut stream = stream.take();
            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = &os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
<doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

\r\n", unsafe{visitor_count});

                    stream.write(response.as_bytes());
                }
                else {
                    println(fmt!("serve file: %?", file_path));
                    match io::read_whole_file(file_path) {
                        Ok(file_data) => {
                            stream.write(file_data);
                        }
                        Err(err) => {
                            println(err);
                        }
                    }
                }
            }
            println!("connection terminates")
        }
    }
}
<commit_msg>Delete zhttpto.rs<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Adapter for `WebPush`.
//!
//! Implemented as described in the draft IETF RFC:
//! https://tools.ietf.org/html/draft-ietf-webpush-protocol-04
//!
//! Encryption and sending of push notifications is controlled by the
//! "webpush" build feature. Older versions of `OpenSSL` (< 1.0.0) are
//! missing the necessary APIs to support the implementation.
//!

mod crypto;
mod db;

use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Type, TypeError, Value, Json };

use hyper::header::{ ContentEncoding, Encoding };
use hyper::Client;
use hyper::client::Body;
use rusqlite::{ self };
use serde_json;
use std::collections::{ HashMap, HashSet };
use std::sync::Arc;
use std::thread;
use traits::Controller;
use transformable_channels::mpsc::*;

header! { (Encryption, "Encryption") => [String] }
header! { (EncryptionKey, "Encryption-Key") => [String] }

static ADAPTER_NAME: &'static str = "WebPush adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];
// This user identifier will be used when authentication is disabled.
static NO_AUTH_USER_ID: i32 = -1;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Subscription {
    pub push_uri: String,
    pub public_key: String
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SubscriptionGetter {
    subscriptions: Vec<Subscription>
}

impl SubscriptionGetter {
    fn new(subs: Vec<Subscription>) -> Self {
        SubscriptionGetter {
            subscriptions: subs
        }
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ResourceGetter {
    resources: Vec<String>
}

impl ResourceGetter {
    fn new(res: Vec<String>) -> Self {
        ResourceGetter {
            resources: res
        }
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct NotifySetter {
    resource: String,
    message: String
}

impl Subscription {
    fn notify(&self, message: &str) {
        let enc = match self::crypto::encrypt(&self.public_key, message.to_owned()) {
            Some(x) => x,
            None => {
                warn!("notity subscription {} failed for {}", self.push_uri, message);
                return;
            }
        };

        let client = Client::new();
        let res = match client.post(&self.push_uri)
            .header(ContentEncoding(vec![Encoding::EncodingExt(String::from("aesgcm128"))]))
            .header(EncryptionKey(format!("keyid=p256dh;dh={}", enc.public_key)))
            .header(Encryption(format!("keyid=p256dh;salt={}", enc.salt)))
            .body(Body::BufBody(&enc.output, enc.output.len()))
            .send() {
                Ok(x) => x,
                Err(e) => { warn!("notify subscription {} failed: {:?}", self.push_uri, e); return; }
            };

        info!("notified subscription {} (status {:?})", self.push_uri, res.status);
    }
}

pub struct WebPush<C> {
    controller: C,
    getter_resource_id: Id<Getter>,
    getter_subscription_id: Id<Getter>,
    setter_resource_id: Id<Setter>,
    setter_subscribe_id: Id<Setter>,
    setter_unsubscribe_id: Id<Setter>,
    setter_notify_id: Id<Setter>,
}

impl<C: Controller> WebPush<C> {
    pub fn id() -> Id<AdapterId> {
        Id::new("webpush@link.mozilla.org")
    }

    pub fn service_webpush_id() -> Id<ServiceId> {
        Id::new("service:webpush@link.mozilla.org")
    }

    pub fn getter_resource_id() -> Id<Getter> {
        Id::new("getter:resource.webpush@link.mozilla.org")
    }

    pub fn getter_subscription_id() -> Id<Getter> {
        Id::new("getter:subscription.webpush@link.mozilla.org")
    }

    pub fn setter_resource_id() -> Id<Setter> {
        Id::new("setter:resource.webpush@link.mozilla.org")
    }

    pub fn setter_subscribe_id() -> Id<Setter> {
        Id::new("setter:subscribe.webpush@link.mozilla.org")
    }

    pub fn setter_unsubscribe_id() -> Id<Setter> {
        Id::new("setter:unsubscribe.webpush@link.mozilla.org")
    }

    pub fn setter_notify_id() -> Id<Setter> {
        Id::new("setter:notify.webpush@link.mozilla.org")
    }
}

impl<C: Controller> Adapter for WebPush<C> {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Getter>>, user: User) -> ResultMap<Id<Getter>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            let user_id = if cfg!(feature = "authentication") {
                match user {
                    User::None => {
                        return (id,
                                Err(Error::InternalError(InternalError::GenericError("Cannot fetch from this channel without a user.".to_owned()))));
                    },
                    User::Id(id) => id
                }
            } else {
                NO_AUTH_USER_ID
            };

            macro_rules! getter_api {
                ($getter:ident, $getter_id:ident, $getter_type:ident) => (
                    if id == self.$getter_id {
                        match self.$getter(user_id) {
                            Ok(data) => {
                                let rsp = $getter_type::new(data);
                                return (id, Ok(Some(Value::Json(Arc::new(Json(serde_json::to_value(&rsp)))))));
                            },
                            Err(err) => return (id, Err(Error::InternalError(InternalError::GenericError(format!("Database error: {}", err)))))
                        };
                    }
                )
            }

            getter_api!(get_subscriptions, getter_subscription_id, SubscriptionGetter);
            getter_api!(get_resources, getter_resource_id, ResourceGetter);
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchGetter(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Setter>, Value>, user: User) -> ResultMap<Id<Setter>, (), Error> {
        values.drain().map(|(id, value)| {
            let user_id = if cfg!(feature = "authentication") {
                match user {
                    User::None => {
                        return (id,
                            Err(Error::InternalError(InternalError::GenericError("Cannot send to this channel without a user.".to_owned()))));
                    },
                    User::Id(id) => id
                }
            } else {
                NO_AUTH_USER_ID
            };

            let arc_json_value = match value {
                Value::Json(v) => v,
                _ => return (id, Err(Error::TypeError(TypeError { expected: Type::Json, got: value.get_type() })))
            };
            let Json(ref json_value) = *arc_json_value;

            macro_rules! setter_api {
                ($setter:ident, $setter_name: expr, $setter_id:ident, $setter_type:ident) => (
                    if id == self.$setter_id {
                        let data: Result<$setter_type, _> = serde_json::from_value(json_value.clone());
                        match data {
                            Ok(x) => {
                                self.$setter(user_id, &x).unwrap();
                                return (id, Ok(()));
                            }
                            Err(err) => return (id, Err(Error::InternalError(InternalError::GenericError(format!("While handling {}, cannot serialize value: {}, {:?}", $setter_name, err, json_value)))))
                        }
                    }
                )
            }

            setter_api!(set_resources, "set_resources", setter_resource_id, ResourceGetter);
            setter_api!(set_subscribe, "set_subscribe", setter_subscribe_id, SubscriptionGetter);
            setter_api!(set_unsubscribe, "set_unsubscribe", setter_unsubscribe_id, SubscriptionGetter);
            setter_api!(set_notify, "set_notify", setter_notify_id, NotifySetter);
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchSetter(id))))
        }).collect()
    }

    fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
    {
        watch.drain(..).map(|(id, _, _)| {
            (id.clone(), Err(Error::GetterDoesNotSupportWatching(id)))
        }).collect()
    }
}

impl<C: Controller> WebPush<C> {
    pub fn init(controller: C, adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let wp = Arc::new(Self::new(controller));
        let id = WebPush::<C>::id();
        let service_id = WebPush::<C>::service_webpush_id();
        let getter_resource_id = wp.getter_resource_id.clone();
        let getter_subscription_id = wp.getter_subscription_id.clone();
        let setter_resource_id = wp.setter_resource_id.clone();
        let setter_subscribe_id = wp.setter_subscribe_id.clone();
        let setter_unsubscribe_id = wp.setter_unsubscribe_id.clone();
        let setter_notify_id = wp.setter_notify_id.clone();

        try!(adapt.add_adapter(wp));
        try!(adapt.add_service(Service::empty(service_id.clone(), id.clone())));

        macro_rules! add_getter {
            ($id:ident, $kind_id:expr) => (
                try!(adapt.add_getter(Channel {
                    tags: HashSet::new(),
                    adapter: id.clone(),
                    id: $id,
                    last_seen: None,
                    service: service_id.clone(),
                    mechanism: Getter {
                        kind: ChannelKind::Extension {
                            vendor: Id::new(ADAPTER_VENDOR),
                            adapter: Id::new(ADAPTER_NAME),
                            kind: Id::new($kind_id),
                            typ: Type::Json,
                        },
                        updated: None
                    }
                }));
            )
        }

        macro_rules! add_setter {
            ($id:ident, $kind_id:expr) => (
                try!(adapt.add_setter(Channel {
                    tags: HashSet::new(),
                    adapter: id.clone(),
                    id: $id,
                    last_seen: None,
                    service: service_id.clone(),
                    mechanism: Setter {
                        kind: ChannelKind::Extension {
                            vendor: Id::new(ADAPTER_VENDOR),
                            adapter: Id::new(ADAPTER_NAME),
                            kind: Id::new($kind_id),
                            typ: Type::Json,
                        },
                        updated: None
                    }
                }));
            )
        }

        try!(adapt.add_setter(Channel {
            tags: HashSet::new(),
            adapter: id.clone(),
            id: setter_notify_id,
            last_seen: None,
            service: service_id.clone(),
            mechanism: Setter {
                kind: ChannelKind::WebPushNotify,
                updated: None
            }
        }));

        add_getter!(getter_resource_id, "WebPushResource");
        add_getter!(getter_subscription_id, "WebPushSubscription");
        add_setter!(setter_resource_id, "WebPushResource");
        add_setter!(setter_subscribe_id, "WebPushSubscription");
        add_setter!(setter_unsubscribe_id, "WebPushSubscription");
        Ok(())
    }

    fn new(controller: C) -> Self
    {
        WebPush {
            controller: controller,
            getter_resource_id: Self::getter_resource_id(),
            getter_subscription_id: Self::getter_subscription_id(),
            setter_resource_id: Self::setter_resource_id(),
            setter_subscribe_id: Self::setter_subscribe_id(),
            setter_unsubscribe_id: Self::setter_unsubscribe_id(),
            setter_notify_id: Self::setter_notify_id(),
        }
    }

    fn get_db(&self) -> db::WebPushDb {
        db::WebPushDb::new(&self.controller.get_profile().path_for("webpush.sqlite"))
    }

    fn set_subscribe(&self, user_id: i32, setter: &SubscriptionGetter) -> rusqlite::Result<()> {
        let db = self.get_db();
        for sub in &setter.subscriptions {
            try!(db.subscribe(user_id, sub));
        }
        Ok(())
    }

    fn set_unsubscribe(&self, user_id: i32, setter: &SubscriptionGetter) -> rusqlite::Result<()> {
        let db = self.get_db();
        for sub in &setter.subscriptions {
            try!(db.unsubscribe(user_id, &sub.push_uri));
        }
        Ok(())
    }

    fn set_resources(&self, user_id: i32, setter: &ResourceGetter) -> rusqlite::Result<()> {
        try!(self.get_db().set_resources(user_id, &setter.resources));
        Ok(())
    }

    fn get_resources(&self, user_id: i32) -> rusqlite::Result<Vec<String>> {
        self.get_db().get_resources(user_id)
    }

    fn get_subscriptions(&self, user_id: i32) -> rusqlite::Result<Vec<Subscription>> {
        self.get_db().get_subscriptions(user_id)
    }

    fn get_resource_subscriptions(&self, resource: &str) -> rusqlite::Result<Vec<Subscription>> {
        self.get_db().get_resource_subscriptions(resource)
    }

    fn set_notify(&self, _: i32, setter: &NotifySetter) -> rusqlite::Result<()> {
        info!("notify on resource {}: {}", setter.resource, setter.message);

        let json = json!({resource: setter.resource, message: setter.message});
        let subscriptions = try!(self.get_resource_subscriptions(&setter.resource));
        if subscriptions.is_empty() {
            debug!("no users listening on push resource");
        } else {
            thread::spawn(move || {
                for sub in subscriptions {
                    sub.notify(&json);
                }
            });
        }
        Ok(())
    }
}
<commit_msg>WebPushNotify now doesn't assume that the payload is unparsed JSON<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Adapter for `WebPush`.
//!
//! Implemented as described in the draft IETF RFC:
//! https://tools.ietf.org/html/draft-ietf-webpush-protocol-04
//!
//! Encryption and sending of push notifications is controlled by the
//! "webpush" build feature. Older versions of `OpenSSL` (< 1.0.0) are
//! missing the necessary APIs to support the implementation.
//!

mod crypto;
mod db;

use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Type, TypeError, Value, Json, WebPushNotify };

use hyper::header::{ ContentEncoding, Encoding };
use hyper::Client;
use hyper::client::Body;
use rusqlite::{ self };
use serde_json;
use std::collections::{ HashMap, HashSet };
use std::sync::Arc;
use std::thread;
use traits::Controller;
use transformable_channels::mpsc::*;

header! { (Encryption, "Encryption") => [String] }
header! { (EncryptionKey, "Encryption-Key") => [String] }

static ADAPTER_NAME: &'static str = "WebPush adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];
// This user identifier will be used when authentication is disabled.
static NO_AUTH_USER_ID: i32 = -1;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Subscription {
    pub push_uri: String,
    pub public_key: String
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SubscriptionGetter {
    subscriptions: Vec<Subscription>
}

impl SubscriptionGetter {
    fn new(subs: Vec<Subscription>) -> Self {
        SubscriptionGetter {
            subscriptions: subs
        }
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ResourceGetter {
    resources: Vec<String>
}

impl ResourceGetter {
    fn new(res: Vec<String>) -> Self {
        ResourceGetter {
            resources: res
        }
    }
}

impl Subscription {
    fn notify(&self, message: &str) {
        let enc = match self::crypto::encrypt(&self.public_key, message.to_owned()) {
            Some(x) => x,
            None => {
                warn!("notity subscription {} failed for {}", self.push_uri, message);
                return;
            }
        };

        let client = Client::new();
        let res = match client.post(&self.push_uri)
            .header(ContentEncoding(vec![Encoding::EncodingExt(String::from("aesgcm128"))]))
            .header(EncryptionKey(format!("keyid=p256dh;dh={}", enc.public_key)))
            .header(Encryption(format!("keyid=p256dh;salt={}", enc.salt)))
            .body(Body::BufBody(&enc.output, enc.output.len()))
            .send() {
                Ok(x) => x,
                Err(e) => { warn!("notify subscription {} failed: {:?}", self.push_uri, e); return; }
            };

        info!("notified subscription {} (status {:?})", self.push_uri, res.status);
    }
}

pub struct WebPush<C> {
    controller: C,
    getter_resource_id: Id<Getter>,
    getter_subscription_id: Id<Getter>,
    setter_resource_id: Id<Setter>,
    setter_subscribe_id: Id<Setter>,
    setter_unsubscribe_id: Id<Setter>,
    setter_notify_id: Id<Setter>,
}

impl<C: Controller> WebPush<C> {
    pub fn id() -> Id<AdapterId> {
        Id::new("webpush@link.mozilla.org")
    }

    pub fn service_webpush_id() -> Id<ServiceId> {
        Id::new("service:webpush@link.mozilla.org")
    }

    pub fn getter_resource_id() -> Id<Getter> {
        Id::new("getter:resource.webpush@link.mozilla.org")
    }

    pub fn getter_subscription_id() -> Id<Getter> {
        Id::new("getter:subscription.webpush@link.mozilla.org")
    }

    pub fn setter_resource_id() -> Id<Setter> {
        Id::new("setter:resource.webpush@link.mozilla.org")
    }

    pub fn setter_subscribe_id() -> Id<Setter> {
        Id::new("setter:subscribe.webpush@link.mozilla.org")
    }

    pub fn setter_unsubscribe_id() -> Id<Setter> {
        Id::new("setter:unsubscribe.webpush@link.mozilla.org")
    }

    pub fn setter_notify_id() -> Id<Setter> {
        Id::new("setter:notify.webpush@link.mozilla.org")
    }
}

impl<C: Controller> Adapter for WebPush<C> {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Getter>>, user: User) -> ResultMap<Id<Getter>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            let user_id = if cfg!(feature = "authentication") {
                match user {
                    User::None => {
                        return (id,
                                Err(Error::InternalError(InternalError::GenericError("Cannot fetch from this channel without a user.".to_owned()))));
                    },
                    User::Id(id) => id
                }
            } else {
                NO_AUTH_USER_ID
            };

            macro_rules! getter_api {
                ($getter:ident, $getter_id:ident, $getter_type:ident) => (
                    if id == self.$getter_id {
                        match self.$getter(user_id) {
                            Ok(data) => {
                                let rsp = $getter_type::new(data);
                                return (id, Ok(Some(Value::Json(Arc::new(Json(serde_json::to_value(&rsp)))))));
                            },
                            Err(err) => return (id, Err(Error::InternalError(InternalError::GenericError(format!("Database error: {}", err)))))
                        };
                    }
                )
            }

            getter_api!(get_subscriptions, getter_subscription_id, SubscriptionGetter);
            getter_api!(get_resources, getter_resource_id, ResourceGetter);
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchGetter(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Setter>, Value>, user: User) -> ResultMap<Id<Setter>, (), Error> {
        values.drain().map(|(id, value)| {
            let user_id = if cfg!(feature = "authentication") {
                match user {
                    User::None => {
                        return (id,
                            Err(Error::InternalError(InternalError::GenericError("Cannot send to this channel without a user.".to_owned()))));
                    },
                    User::Id(id) => id
                }
            } else {
                NO_AUTH_USER_ID
            };

            if id == self.setter_notify_id {
                match value {
                    Value::WebPushNotify(notification) => {
                        match self.set_notify(user_id, &notification) {
                            Ok(_) => return (id, Ok(())),
                            Err(err) => return (id, Err(Error::InternalError(InternalError::GenericError(format!("Database error: {}", err)))))
                        }
                    },
                   _ => return (id, Err(Error::TypeError(TypeError { expected: Type::WebPushNotify, got: value.get_type() })))
                }
            }

            let arc_json_value = match value {
                Value::Json(v) => v,
                _ => return (id, Err(Error::TypeError(TypeError { expected: Type::Json, got: value.get_type() })))
            };
            let Json(ref json_value) = *arc_json_value;

            macro_rules! setter_api {
                ($setter:ident, $setter_name: expr, $setter_id:ident, $setter_type:ident) => (
                    if id == self.$setter_id {
                        let data: Result<$setter_type, _> = serde_json::from_value(json_value.clone());
                        match data {
                            Ok(x) => {
                                self.$setter(user_id, &x).unwrap();
                                return (id, Ok(()));
                            }
                            Err(err) => return (id, Err(Error::InternalError(InternalError::GenericError(format!("While handling {}, cannot serialize value: {}, {:?}", $setter_name, err, json_value)))))
                        }
                    }
                )
            }

            setter_api!(set_resources, "set_resources", setter_resource_id, ResourceGetter);
            setter_api!(set_subscribe, "set_subscribe", setter_subscribe_id, SubscriptionGetter);
            setter_api!(set_unsubscribe, "set_unsubscribe", setter_unsubscribe_id, SubscriptionGetter);
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchSetter(id))))
        }).collect()
    }

    fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
    {
        watch.drain(..).map(|(id, _, _)| {
            (id.clone(), Err(Error::GetterDoesNotSupportWatching(id)))
        }).collect()
    }
}

impl<C: Controller> WebPush<C> {
    pub fn init(controller: C, adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let wp = Arc::new(Self::new(controller));
        let id = WebPush::<C>::id();
        let service_id = WebPush::<C>::service_webpush_id();
        let getter_resource_id = wp.getter_resource_id.clone();
        let getter_subscription_id = wp.getter_subscription_id.clone();
        let setter_resource_id = wp.setter_resource_id.clone();
        let setter_subscribe_id = wp.setter_subscribe_id.clone();
        let setter_unsubscribe_id = wp.setter_unsubscribe_id.clone();
        let setter_notify_id = wp.setter_notify_id.clone();

        try!(adapt.add_adapter(wp));
        try!(adapt.add_service(Service::empty(service_id.clone(), id.clone())));

        macro_rules! add_getter {
            ($id:ident, $kind_id:expr) => (
                try!(adapt.add_getter(Channel {
                    tags: HashSet::new(),
                    adapter: id.clone(),
                    id: $id,
                    last_seen: None,
                    service: service_id.clone(),
                    mechanism: Getter {
                        kind: ChannelKind::Extension {
                            vendor: Id::new(ADAPTER_VENDOR),
                            adapter: Id::new(ADAPTER_NAME),
                            kind: Id::new($kind_id),
                            typ: Type::Json,
                        },
                        updated: None
                    }
                }));
            )
        }

        macro_rules! add_setter {
            ($id:ident, $kind_id:expr) => (
                try!(adapt.add_setter(Channel {
                    tags: HashSet::new(),
                    adapter: id.clone(),
                    id: $id,
                    last_seen: None,
                    service: service_id.clone(),
                    mechanism: Setter {
                        kind: ChannelKind::Extension {
                            vendor: Id::new(ADAPTER_VENDOR),
                            adapter: Id::new(ADAPTER_NAME),
                            kind: Id::new($kind_id),
                            typ: Type::Json,
                        },
                        updated: None
                    }
                }));
            )
        }

        try!(adapt.add_setter(Channel {
            tags: HashSet::new(),
            adapter: id.clone(),
            id: setter_notify_id,
            last_seen: None,
            service: service_id.clone(),
            mechanism: Setter {
                kind: ChannelKind::WebPushNotify,
                updated: None
            }
        }));

        add_getter!(getter_resource_id, "WebPushResource");
        add_getter!(getter_subscription_id, "WebPushSubscription");
        add_setter!(setter_resource_id, "WebPushResource");
        add_setter!(setter_subscribe_id, "WebPushSubscription");
        add_setter!(setter_unsubscribe_id, "WebPushSubscription");
        Ok(())
    }

    fn new(controller: C) -> Self
    {
        WebPush {
            controller: controller,
            getter_resource_id: Self::getter_resource_id(),
            getter_subscription_id: Self::getter_subscription_id(),
            setter_resource_id: Self::setter_resource_id(),
            setter_subscribe_id: Self::setter_subscribe_id(),
            setter_unsubscribe_id: Self::setter_unsubscribe_id(),
            setter_notify_id: Self::setter_notify_id(),
        }
    }

    fn get_db(&self) -> db::WebPushDb {
        db::WebPushDb::new(&self.controller.get_profile().path_for("webpush.sqlite"))
    }

    fn set_subscribe(&self, user_id: i32, setter: &SubscriptionGetter) -> rusqlite::Result<()> {
        let db = self.get_db();
        for sub in &setter.subscriptions {
            try!(db.subscribe(user_id, sub));
        }
        Ok(())
    }

    fn set_unsubscribe(&self, user_id: i32, setter: &SubscriptionGetter) -> rusqlite::Result<()> {
        let db = self.get_db();
        for sub in &setter.subscriptions {
            try!(db.unsubscribe(user_id, &sub.push_uri));
        }
        Ok(())
    }

    fn set_resources(&self, user_id: i32, setter: &ResourceGetter) -> rusqlite::Result<()> {
        try!(self.get_db().set_resources(user_id, &setter.resources));
        Ok(())
    }

    fn get_resources(&self, user_id: i32) -> rusqlite::Result<Vec<String>> {
        self.get_db().get_resources(user_id)
    }

    fn get_subscriptions(&self, user_id: i32) -> rusqlite::Result<Vec<Subscription>> {
        self.get_db().get_subscriptions(user_id)
    }

    fn get_resource_subscriptions(&self, resource: &str) -> rusqlite::Result<Vec<Subscription>> {
        self.get_db().get_resource_subscriptions(resource)
    }

    fn set_notify(&self, _: i32, setter: &WebPushNotify) -> rusqlite::Result<()> {
        info!("notify on resource {}: {}", setter.resource, setter.message);

        let json = json!({resource: setter.resource, message: setter.message});
        let subscriptions = try!(self.get_resource_subscriptions(&setter.resource));
        if subscriptions.is_empty() {
            debug!("no users listening on push resource");
        } else {
            thread::spawn(move || {
                for sub in subscriptions {
                    sub.notify(&json);
                }
            });
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The Stainless Developers. See the LICENSE file at the top-level directory of
// this distrubution.
//
// Licensed under the MIT license. This file may not be copied, modified, or distributed except
// according to those terms.

/// ## Internal Code Guide
///
/// This crate is centered around two traits, Parse and Generate,
/// which define how to parse a struct from a Parser over Rust code
/// and how to generate Rust code from a struct respectively.
///
/// All the parsing and generation of code is done through these traits.
///
/// `describe` is responsible for expanding the `describe!` macro
/// as a whole, and delegates to the Parse and Generate implementations
/// for `DescribeState`, which holds all the information necessary to
/// generate the AST for an expanded `describe!`.
///
/// The Parse implementation of DescribeState delegates to the Parse
/// implementation of Test, defined in the test module, and Bench,
/// defined in the bench module. The Generate implementation does the
/// same.
///
/// Most of the code can be understood by just walking through the
/// implementations of Parse and Generate for all the types inside
/// this crate, which can be found in the parse and generate
/// modules respectively.
///

use syntax::{ast, codemap, parse, tokenstream};
use syntax::ptr::P;
use syntax::ext::base;
use syntax::util::small_vector::SmallVector;

use parse::Parse;
use generate::Generate;
use test::Test;
use bench::Bench;

/// Defines the state of a `describe!` macro as it is parsing.
#[derive(Clone)]
pub struct DescribeState {
    pub name: Option<ast::Ident>,
    pub before: Option<P<ast::Block>>,
    pub after: Option<P<ast::Block>>,
    pub before_each: Option<P<ast::Block>>,
    pub after_each: Option<P<ast::Block>>,
    pub subblocks: Vec<SubBlock>
}

/// Any supported subblock.
#[derive(Clone)]
pub enum SubBlock {
    Test(Test),
    Bench(Bench),
    Describe(DescribeState)
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe<'a>(cx: &'a mut base::ExtCtxt, sp: codemap::Span,
                name: ast::Ident, tokens: Vec<tokenstream::TokenTree>) -> Box<base::MacResult + 'a> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state: DescribeState = Parse::parse(&mut parse::tts_to_parser(cx.parse_sess(), tokens, cx.cfg()), (sp, &mut*cx, Some(name)));

    // Export the new module.
    base::MacEager::items(SmallVector::one(state.generate(sp, cx, None)))
}
<commit_msg>Fix for `rustc 1.14.0-nightly (f09420685 2016-10-20)`.<commit_after>// Copyright 2014-2015 The Stainless Developers. See the LICENSE file at the top-level directory of
// this distrubution.
//
// Licensed under the MIT license. This file may not be copied, modified, or distributed except
// according to those terms.

/// ## Internal Code Guide
///
/// This crate is centered around two traits, Parse and Generate,
/// which define how to parse a struct from a Parser over Rust code
/// and how to generate Rust code from a struct respectively.
///
/// All the parsing and generation of code is done through these traits.
///
/// `describe` is responsible for expanding the `describe!` macro
/// as a whole, and delegates to the Parse and Generate implementations
/// for `DescribeState`, which holds all the information necessary to
/// generate the AST for an expanded `describe!`.
///
/// The Parse implementation of DescribeState delegates to the Parse
/// implementation of Test, defined in the test module, and Bench,
/// defined in the bench module. The Generate implementation does the
/// same.
///
/// Most of the code can be understood by just walking through the
/// implementations of Parse and Generate for all the types inside
/// this crate, which can be found in the parse and generate
/// modules respectively.
///

use syntax::{ast, codemap, parse, tokenstream};
use syntax::ptr::P;
use syntax::ext::base;
use syntax::util::small_vector::SmallVector;

use parse::Parse;
use generate::Generate;
use test::Test;
use bench::Bench;

/// Defines the state of a `describe!` macro as it is parsing.
#[derive(Clone)]
pub struct DescribeState {
    pub name: Option<ast::Ident>,
    pub before: Option<P<ast::Block>>,
    pub after: Option<P<ast::Block>>,
    pub before_each: Option<P<ast::Block>>,
    pub after_each: Option<P<ast::Block>>,
    pub subblocks: Vec<SubBlock>
}

/// Any supported subblock.
#[derive(Clone)]
pub enum SubBlock {
    Test(Test),
    Bench(Bench),
    Describe(DescribeState)
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe<'a>(cx: &'a mut base::ExtCtxt, sp: codemap::Span,
                name: ast::Ident, tokens: Vec<tokenstream::TokenTree>) -> Box<base::MacResult + 'a> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state: DescribeState = Parse::parse(&mut parse::tts_to_parser(cx.parse_sess(), tokens, cx.cfg().clone()), (sp, &mut*cx, Some(name)));

    // Export the new module.
    base::MacEager::items(SmallVector::one(state.generate(sp, cx, None)))
}
<|endoftext|>