<commit_before>use std::rc::Rc;
use std::cell::RefCell;

use ast::*;
use value::*;
use operations;
use environment::Environment;
use typechecker::Type;
use function::*;

#[derive(Debug, PartialEq)]
pub enum InterpreterError {
    /// When an undeclared identifier is used on the RHS
    ReferenceError(String),
    /// When an undeclared identifier is assigned to
    UndeclaredAssignment(String),
    /// When a binary op cannot be performed on the given types
    BinaryTypeError(BinaryOp, Type, Type),
    /// When a unary op cannot be performed on the given type
    UnaryTypeError(UnaryOp, Type),
    /// When a non-returning function's return value is used
    NoneError(Option<String>),
    /// When a call is made to a non-function value
    CallToNonFunction(Option<String>, Type),
    /// When the number of arguments don't match
    ArgumentLength(Option<String>),
}

pub type InterpreterErrorWithPosition = (InterpreterError, OffsetSpan);

#[derive(Debug, PartialEq)]
pub enum StatementResult {
    None,
    Break,
    Value(Value),
    Return(Option<Value>),
}

pub struct Interpreter {
    pub root_env: Rc<RefCell<Environment>>,
}

impl Interpreter {
    pub fn new() -> Interpreter {
        Interpreter { root_env: Environment::new_root() }
    }

    pub fn run_ast_as_program(&mut self,
                              program: &[StatementNode])
                              -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
        interpret_program(program, self.root_env.clone())
    }

    pub fn run_ast_as_statements
        (&mut self,
         statements: &[StatementNode])
         -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
        interpret_statements(statements, self.root_env.clone())
    }
}

fn interpret_program(program: &[StatementNode],
                     env: Rc<RefCell<Environment>>)
                     -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
    let result = interpret_statements(program, env.clone())?;
    Ok(result)
}

fn interpret_statements(statements: &[StatementNode],
                        env: Rc<RefCell<Environment>>)
                        -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
    let mut last_result = None;
    for statement in statements.iter() {
        last_result = Some(interpret_statement(statement, env.clone())?);
    }
    Ok(last_result)
}

fn interpret_statement(s: &StatementNode,
                       env: Rc<RefCell<Environment>>)
                       -> Result<StatementResult, InterpreterErrorWithPosition> {
    match s.data {
        Statement::VariableDeclaration(ref variable, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match *variable {
                Variable::Identifier(_, ref name) => {
                    env.borrow_mut().declare(name, &val);
                }
            };
            Ok(StatementResult::None)
        }
        Statement::Assignment(ref lhs_expr, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match lhs_expr.data {
                LhsExpr::Identifier(ref id) => {
                    if !env.borrow_mut().set(id, val) {
                        return Err((InterpreterError::UndeclaredAssignment(id.clone()),
                                    lhs_expr.pos));
                    }
                }
            };
            Ok(StatementResult::None)
        }
        Statement::Block(ref statements) => {
            let child_env = Environment::create_child(env.clone());
            let mut last_result = StatementResult::None;
            for statement in statements.iter() {
                last_result = interpret_statement(statement, child_env.clone())?;
                if let StatementResult::Break = last_result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = last_result {
                    return Ok(last_result);
                }
            }
            Ok(last_result)
        }
        Statement::Expression(ref expr) => {
            let val = interpret_expr(expr, env.clone())?;
            match val {
                None => Ok(StatementResult::None),
                Some(x) => Ok(StatementResult::Value(x)),
            }
        }
        Statement::IfThen(ref if_expr, ref then_block) => {
            let possible_val = interpret_expr(if_expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, if_expr)?;
            if val.is_truthy() {
                let result = interpret_statement(then_block, env.clone())?;
                if let StatementResult::Break = result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            }
            Ok(StatementResult::None)
        }
        Statement::IfThenElse(ref if_expr, ref then_block, ref else_block) => {
            let possible_val = interpret_expr(if_expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, if_expr)?;
            if val.is_truthy() {
                let result = interpret_statement(then_block, env.clone())?;
                if let StatementResult::Break = result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            } else {
                let result = interpret_statement(else_block, env.clone())?;
                if let StatementResult::Break = result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            }
            Ok(StatementResult::None)
        }
        Statement::Loop(ref block) => {
            let child_env = Environment::create_child(env.clone());
            loop {
                let result = interpret_statement(block, child_env.clone())?;
                if let StatementResult::Break = result {
                    break;
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            }
            Ok(StatementResult::None)
        }
        Statement::Return(ref possible_expr) => {
            match *possible_expr {
                Some(ref expr) => {
                    let possible_val = interpret_expr(expr, env.clone())?;
                    let val = check_val_for_none_error(&possible_val, expr)?;
                    Ok(StatementResult::Return(Some(val)))
                }
                None => Ok(StatementResult::Return(None)),
            }
        }
        Statement::Break => Ok(StatementResult::Break),
        Statement::Empty => Ok(StatementResult::None),
    }
}
fn interpret_expr(e: &ExprNode,
                  env: Rc<RefCell<Environment>>)
                  -> Result<Option<Value>, InterpreterErrorWithPosition> {
    match e.data {
        Expr::Literal(ref x) => Ok(Some(Value::from(x.clone()))),
        Expr::Identifier(ref id) => {
            match env.borrow_mut().get_value(id) {
                Some(v) => Ok(Some(v)),
                None => Err((InterpreterError::ReferenceError(id.clone()), e.pos)),
            }
        }
        Expr::Tuple(ref elems) => {
            let mut values = Vec::new();
            for elem_expr in elems {
                let possible_val = interpret_expr(elem_expr, env.clone())?;
                let val = check_val_for_none_error(&possible_val, elem_expr)?;
                values.push(val);
            }
            Ok(Some(Value::Tuple(values)))
        }
        Expr::UnaryExpression(ref op, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match *op {
                UnaryOp::Minus => {
                    match operations::unary_minus(val) {
                        Ok(v) => Ok(Some(v)),
                        Err(err) => Err((err, e.pos)),
                    }
                }
            }
        }
        Expr::UnaryLogicalExpression(ref op, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match *op {
                LogicalUnaryOp::Not => Ok(Some(Value::Bool(!val.is_truthy()))),
            }
        }
        Expr::BinaryExpression(ref expr1, ref op, ref expr2) => {
            let possible_val_1 = interpret_expr(expr1, env.clone())?;
            let val1 = check_val_for_none_error(&possible_val_1, expr1)?;
            let possible_val_2 = interpret_expr(expr2, env.clone())?;
            let val2 = check_val_for_none_error(&possible_val_2, expr2)?;
            let retval = match *op {
                BinaryOp::Add => operations::add(val1, val2),
                BinaryOp::Sub => operations::subtract(val1, val2),
                BinaryOp::Mul => operations::multiply(val1, val2),
                BinaryOp::Div => operations::divide(val1, val2),
                BinaryOp::FloorDiv => operations::floor_divide(val1, val2),
                BinaryOp::LessThan => operations::less_than(val1, val2),
                BinaryOp::LessThanOrEqual => operations::less_than_or_equal(val1, val2),
                BinaryOp::GreaterThan => operations::greater_than(val1, val2),
                BinaryOp::GreaterThanOrEqual => operations::greater_than_or_equal(val1, val2),
                BinaryOp::StrictEquals => Ok(Value::Bool(val1 == val2)),
            };
            match retval {
                Ok(v) => Ok(Some(v)),
                Err(err) => Err((err, e.pos)),
            }
        }
        Expr::BinaryLogicalExpression(ref expr1, ref op, ref expr2) => {
            match *op {
                LogicalBinaryOp::LogicalAnd => {
                    let possible_val_1 = interpret_expr(expr1, env.clone())?;
                    let val1 = check_val_for_none_error(&possible_val_1, expr1)?;
                    if !val1.is_truthy() {
                        return Ok(Some(Value::Bool(false)));
                    }
                    let possible_val_2 = interpret_expr(expr2, env.clone())?;
                    let val2 = check_val_for_none_error(&possible_val_2, expr2)?;
                    Ok(Some(Value::Bool(val2.is_truthy())))
                }
                LogicalBinaryOp::LogicalOr => {
                    let possible_val_1 = interpret_expr(expr1, env.clone())?;
                    let val1 = check_val_for_none_error(&possible_val_1, expr1)?;
                    if val1.is_truthy() {
                        return Ok(Some(Value::Bool(true)));
                    }
                    let possible_val_2 = interpret_expr(expr2, env.clone())?;
                    let val2 = check_val_for_none_error(&possible_val_2, expr2)?;
                    Ok(Some(Value::Bool(val2.is_truthy())))
                }
            }
        }
        Expr::FunctionDefinition(ref possible_id, ref param_list, ref body) => {
            let func = Function::User {
                // FIXME: returning is no longer always false
                returning: false,
                call_sign: CallSign {
                    num_params: param_list.len(),
                    variadic: false,
                },
                param_list: param_list.clone(),
                body: body.clone(),
                env: env.clone(),
            };
            let func_val = Value::Function(func);
            if let Some(ref id) = *possible_id {
                env.borrow_mut().declare(id, &func_val);
            }
            Ok(Some(func_val))
        }
        Expr::FunctionCall(ref expr, ref args) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            let func = match val {
                Value::Function(f) => f,
                v => {
                    if let Expr::Identifier(ref id) = expr.data {
                        return Err((InterpreterError::CallToNonFunction(Some(id.clone()),
                                                                        v.get_type()),
                                    e.pos));
                    }
                    return Err((InterpreterError::CallToNonFunction(None, v.get_type()), e.pos));
                }
            };

            let mut arg_vals = Vec::new();
            for arg in args.iter() {
                let possible_val = interpret_expr(arg, env.clone())?;
                let val = check_val_for_none_error(&possible_val, arg)?;
                arg_vals.push(val);
            }

            let call_sign = func.get_call_sign();
            check_args_compat(&arg_vals, &call_sign, e)?;

            match func {
                Function::NativeVoid(_, native_fn) => {
                    native_fn(arg_vals);
                    Ok(None)
                }
                Function::NativeReturning(_, native_fn) => Ok(Some(native_fn(arg_vals))),
                Function::User { param_list, body, env, .. } => {
                    // TODO: returning
                    let function_env = Environment::create_child(env.clone());
                    for (param, arg) in param_list.iter().zip(arg_vals.iter()) {
                        function_env.borrow_mut().declare(param, arg);
                    }
                    let inner_env = Environment::create_child(function_env);
                    if let StatementResult::Return(possible_val) =
                        interpret_statement(&body, inner_env)? {
                        match possible_val {
                            Some(val) => Ok(Some(val)),
                            None => Ok(None),
                        }
                    } else {
                        Ok(None)
                    }
                }
            }
        }
    }
}

fn check_val_for_none_error(val: &Option<Value>,
                            expr: &ExprNode)
                            -> Result<Value, InterpreterErrorWithPosition> {
    if val.is_none() {
        if let Expr::FunctionCall(ref f_expr, _) = expr.data {
            if let Expr::Identifier(ref id) = f_expr.data {
                return Err((InterpreterError::NoneError(Some(id.clone())), expr.pos));
            }
            return Err((InterpreterError::NoneError(None), expr.pos));
        }
    }
    Ok(val.clone().unwrap())
}

fn check_args_compat(arg_vals: &[Value],
                     call_sign: &CallSign,
                     expr: &ExprNode)
                     -> Result<(), InterpreterErrorWithPosition> {
    if !call_sign.variadic && call_sign.num_params != arg_vals.len() {
        if let Expr::Identifier(ref id) = expr.data {
            return Err((InterpreterError::ArgumentLength(Some(id.clone())), expr.pos));
        }
        return Err((InterpreterError::ArgumentLength(None), expr.pos));
    }
    Ok(())
}
<commit_msg>Move logic of calling function into separate fn<commit_after>use std::rc::Rc;
use std::cell::RefCell;

use ast::*;
use value::*;
use operations;
use environment::Environment;
use typechecker::Type;
use function::*;

#[derive(Debug, PartialEq)]
pub enum InterpreterError {
    /// When an undeclared identifier is used on the RHS
    ReferenceError(String),
    /// When an undeclared identifier is assigned to
    UndeclaredAssignment(String),
    /// When a binary op cannot be performed on the given types
    BinaryTypeError(BinaryOp, Type, Type),
    /// When a unary op cannot be performed on the given type
    UnaryTypeError(UnaryOp, Type),
    /// When a non-returning function's return value is used
    NoneError(Option<String>),
    /// When a call is made to a non-function value
    CallToNonFunction(Option<String>, Type),
    /// When the number of arguments don't match
    ArgumentLength(Option<String>),
}

pub type InterpreterErrorWithPosition = (InterpreterError, OffsetSpan);

#[derive(Debug, PartialEq)]
pub enum StatementResult {
    None,
    Break,
    Value(Value),
    Return(Option<Value>),
}

pub struct Interpreter {
    pub root_env: Rc<RefCell<Environment>>,
}

impl Interpreter {
    pub fn new() -> Interpreter {
        Interpreter { root_env: Environment::new_root() }
    }

    pub fn run_ast_as_program(&mut self,
                              program: &[StatementNode])
                              -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
        interpret_program(program, self.root_env.clone())
    }

    pub fn run_ast_as_statements
        (&mut self,
         statements: &[StatementNode])
         -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
        interpret_statements(statements, self.root_env.clone())
    }
}

fn interpret_program(program: &[StatementNode],
                     env: Rc<RefCell<Environment>>)
                     -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
    let result = interpret_statements(program, env.clone())?;
    Ok(result)
}

fn interpret_statements(statements: &[StatementNode],
                        env: Rc<RefCell<Environment>>)
                        -> Result<Option<StatementResult>, InterpreterErrorWithPosition> {
    let mut last_result = None;
    for statement in statements.iter() {
        last_result = Some(interpret_statement(statement, env.clone())?);
    }
    Ok(last_result)
}

fn interpret_statement(s: &StatementNode,
                       env: Rc<RefCell<Environment>>)
                       -> Result<StatementResult, InterpreterErrorWithPosition> {
    match s.data {
        Statement::VariableDeclaration(ref variable, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match *variable {
                Variable::Identifier(_, ref name) => {
                    env.borrow_mut().declare(name, &val);
                }
            };
            Ok(StatementResult::None)
        }
        Statement::Assignment(ref lhs_expr, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match lhs_expr.data {
                LhsExpr::Identifier(ref id) => {
                    if !env.borrow_mut().set(id, val) {
                        return Err((InterpreterError::UndeclaredAssignment(id.clone()),
                                    lhs_expr.pos));
                    }
                }
            };
            Ok(StatementResult::None)
        }
        Statement::Block(ref statements) => {
            let child_env = Environment::create_child(env.clone());
            let mut last_result = StatementResult::None;
            for statement in statements.iter() {
                last_result = interpret_statement(statement, child_env.clone())?;
                if let StatementResult::Break = last_result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = last_result {
                    return Ok(last_result);
                }
            }
            Ok(last_result)
        }
        Statement::Expression(ref expr) => {
            let val = interpret_expr(expr, env.clone())?;
            match val {
                None => Ok(StatementResult::None),
                Some(x) => Ok(StatementResult::Value(x)),
            }
        }
        Statement::IfThen(ref if_expr, ref then_block) => {
            let possible_val = interpret_expr(if_expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, if_expr)?;
            if val.is_truthy() {
                let result = interpret_statement(then_block, env.clone())?;
                if let StatementResult::Break = result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            }
            Ok(StatementResult::None)
        }
        Statement::IfThenElse(ref if_expr, ref then_block, ref else_block) => {
            let possible_val = interpret_expr(if_expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, if_expr)?;
            if val.is_truthy() {
                let result = interpret_statement(then_block, env.clone())?;
                if let StatementResult::Break = result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            } else {
                let result = interpret_statement(else_block, env.clone())?;
                if let StatementResult::Break = result {
                    return Ok(StatementResult::Break);
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            }
            Ok(StatementResult::None)
        }
        Statement::Loop(ref block) => {
            let child_env = Environment::create_child(env.clone());
            loop {
                let result = interpret_statement(block, child_env.clone())?;
                if let StatementResult::Break = result {
                    break;
                } else if let StatementResult::Return(_) = result {
                    return Ok(result);
                }
            }
            Ok(StatementResult::None)
        }
        Statement::Return(ref possible_expr) => {
            match *possible_expr {
                Some(ref expr) => {
                    let possible_val = interpret_expr(expr, env.clone())?;
                    let val = check_val_for_none_error(&possible_val, expr)?;
                    Ok(StatementResult::Return(Some(val)))
                }
                None => Ok(StatementResult::Return(None)),
            }
        }
        Statement::Break => Ok(StatementResult::Break),
        Statement::Empty => Ok(StatementResult::None),
    }
}
fn interpret_expr(e: &ExprNode,
                  env: Rc<RefCell<Environment>>)
                  -> Result<Option<Value>, InterpreterErrorWithPosition> {
    match e.data {
        Expr::Literal(ref x) => Ok(Some(Value::from(x.clone()))),
        Expr::Identifier(ref id) => {
            match env.borrow_mut().get_value(id) {
                Some(v) => Ok(Some(v)),
                None => Err((InterpreterError::ReferenceError(id.clone()), e.pos)),
            }
        }
        Expr::Tuple(ref elems) => {
            let mut values = Vec::new();
            for elem_expr in elems {
                let possible_val = interpret_expr(elem_expr, env.clone())?;
                let val = check_val_for_none_error(&possible_val, elem_expr)?;
                values.push(val);
            }
            Ok(Some(Value::Tuple(values)))
        }
        Expr::UnaryExpression(ref op, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match *op {
                UnaryOp::Minus => {
                    match operations::unary_minus(val) {
                        Ok(v) => Ok(Some(v)),
                        Err(err) => Err((err, e.pos)),
                    }
                }
            }
        }
        Expr::UnaryLogicalExpression(ref op, ref expr) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            match *op {
                LogicalUnaryOp::Not => Ok(Some(Value::Bool(!val.is_truthy()))),
            }
        }
        Expr::BinaryExpression(ref expr1, ref op, ref expr2) => {
            let possible_val_1 = interpret_expr(expr1, env.clone())?;
            let val1 = check_val_for_none_error(&possible_val_1, expr1)?;
            let possible_val_2 = interpret_expr(expr2, env.clone())?;
            let val2 = check_val_for_none_error(&possible_val_2, expr2)?;
            let retval = match *op {
                BinaryOp::Add => operations::add(val1, val2),
                BinaryOp::Sub => operations::subtract(val1, val2),
                BinaryOp::Mul => operations::multiply(val1, val2),
                BinaryOp::Div => operations::divide(val1, val2),
                BinaryOp::FloorDiv => operations::floor_divide(val1, val2),
                BinaryOp::LessThan => operations::less_than(val1, val2),
                BinaryOp::LessThanOrEqual => operations::less_than_or_equal(val1, val2),
                BinaryOp::GreaterThan => operations::greater_than(val1, val2),
                BinaryOp::GreaterThanOrEqual => operations::greater_than_or_equal(val1, val2),
                BinaryOp::StrictEquals => Ok(Value::Bool(val1 == val2)),
            };
            match retval {
                Ok(v) => Ok(Some(v)),
                Err(err) => Err((err, e.pos)),
            }
        }
        Expr::BinaryLogicalExpression(ref expr1, ref op, ref expr2) => {
            match *op {
                LogicalBinaryOp::LogicalAnd => {
                    let possible_val_1 = interpret_expr(expr1, env.clone())?;
                    let val1 = check_val_for_none_error(&possible_val_1, expr1)?;
                    if !val1.is_truthy() {
                        return Ok(Some(Value::Bool(false)));
                    }
                    let possible_val_2 = interpret_expr(expr2, env.clone())?;
                    let val2 = check_val_for_none_error(&possible_val_2, expr2)?;
                    Ok(Some(Value::Bool(val2.is_truthy())))
                }
                LogicalBinaryOp::LogicalOr => {
                    let possible_val_1 = interpret_expr(expr1, env.clone())?;
                    let val1 = check_val_for_none_error(&possible_val_1, expr1)?;
                    if val1.is_truthy() {
                        return Ok(Some(Value::Bool(true)));
                    }
                    let possible_val_2 = interpret_expr(expr2, env.clone())?;
                    let val2 = check_val_for_none_error(&possible_val_2, expr2)?;
                    Ok(Some(Value::Bool(val2.is_truthy())))
                }
            }
        }
        Expr::FunctionDefinition(ref possible_id, ref param_list, ref body) => {
            let func = Function::User {
                // FIXME: returning is no longer always false
                returning: false,
                call_sign: CallSign {
                    num_params: param_list.len(),
                    variadic: false,
                },
                param_list: param_list.clone(),
                body: body.clone(),
                env: env.clone(),
            };
            let func_val = Value::Function(func);
            if let Some(ref id) = *possible_id {
                env.borrow_mut().declare(id, &func_val);
            }
            Ok(Some(func_val))
        }
        Expr::FunctionCall(ref expr, ref args) => {
            let possible_val = interpret_expr(expr, env.clone())?;
            let val = check_val_for_none_error(&possible_val, expr)?;
            let func = match val {
                Value::Function(f) => f,
                v => {
                    if let Expr::Identifier(ref id) = expr.data {
                        return Err((InterpreterError::CallToNonFunction(Some(id.clone()),
                                                                        v.get_type()),
                                    e.pos));
                    }
                    return Err((InterpreterError::CallToNonFunction(None, v.get_type()), e.pos));
                }
            };
            let mut arg_vals = Vec::new();
            for arg in args.iter() {
                let possible_val = interpret_expr(arg, env.clone())?;
                let val = check_val_for_none_error(&possible_val, arg)?;
                arg_vals.push(val);
            }

            let call_sign = func.get_call_sign();
            check_args_compat(&arg_vals, &call_sign, e)?;

            call_func(&func, &arg_vals)
        }
    }
}

pub fn call_func(func: &Function, arg_vals: &Vec<Value>) -> Result<Option<Value>, InterpreterErrorWithPosition> {
    match *func {
        Function::NativeVoid(_, ref native_fn) => {
            native_fn(arg_vals.clone());
            Ok(None)
        }
        Function::NativeReturning(_, ref native_fn) => Ok(Some(native_fn(arg_vals.clone()))),
        Function::User { ref param_list, ref body, ref env, .. } => {
            // TODO: returning
            let function_env = Environment::create_child(env.clone());
            for (param, arg) in param_list.iter().zip(arg_vals.iter()) {
                function_env.borrow_mut().declare(param, arg);
            }
            let inner_env = Environment::create_child(function_env);
            if let StatementResult::Return(possible_val) =
                interpret_statement(&body, inner_env)? {
                match possible_val {
                    Some(val) => Ok(Some(val)),
                    None => Ok(None),
                }
            } else {
                Ok(None)
            }
        }
    }
}

fn check_val_for_none_error(val: &Option<Value>,
                            expr: &ExprNode)
                            -> Result<Value, InterpreterErrorWithPosition> {
    if val.is_none() {
        if let Expr::FunctionCall(ref f_expr, _) = expr.data {
            if let Expr::Identifier(ref id) = f_expr.data {
                return Err((InterpreterError::NoneError(Some(id.clone())), expr.pos));
            }
            return Err((InterpreterError::NoneError(None), expr.pos));
        }
    }
    Ok(val.clone().unwrap())
}

fn check_args_compat(arg_vals: &[Value],
                     call_sign: &CallSign,
                     expr: &ExprNode)
                     -> Result<(), InterpreterErrorWithPosition> {
    if !call_sign.variadic && call_sign.num_params != arg_vals.len() {
        if let Expr::Identifier(ref id) = expr.data {
            return Err((InterpreterError::ArgumentLength(Some(id.clone())), expr.pos));
        }
        return Err((InterpreterError::ArgumentLength(None), expr.pos));
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Populate a set of dummy download statistics for a specific version in the
// database.
//
// Usage:
//      cargo run --bin populate version_id1 version_id2 ...

#![deny(warnings)]

extern crate cargo_registry;
extern crate postgres;
extern crate time;
extern crate rand;

use std::env;
use time::Duration;
use rand::{StdRng, Rng};

#[allow(dead_code)]
fn main() {
    let conn = cargo_registry::db::connect_now_old();
    {
        let tx = conn.transaction().unwrap();
        update(&tx).unwrap();
        tx.set_commit();
        tx.finish().unwrap();
    }
}

fn update(tx: &postgres::transaction::Transaction) -> postgres::Result<()> {
    let ids = env::args().skip(1).filter_map(
        |arg| arg.parse::<i32>().ok(),
    );
    for id in ids {
        let now = time::now_utc().to_timespec();
        let mut rng = StdRng::new().unwrap();
        let mut dls = rng.gen_range(5000i32, 10000);

        for day in 0..90 {
            let moment = now + Duration::days(-day);
            dls += rng.gen_range(-100, 100);
            tx.execute(
                "INSERT INTO version_downloads \
                 (version_id, downloads, date) \
                 VALUES ($1, $2, $3)",
                &[&id, &dls, &moment],
            )?;
        }
    }
    Ok(())
}
<commit_msg>Port `bin/populate` over to use Diesel<commit_after>// Populate a set of dummy download statistics for a specific version in the
// database.
//
// Usage:
//      cargo run --bin populate version_id1 version_id2 ...

#![deny(warnings)]

extern crate cargo_registry;
extern crate chrono;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate rand;

use chrono::{Utc, NaiveDate, Duration};
use diesel::pg::PgConnection;
use diesel::prelude::*;
use rand::{StdRng, Rng};
use std::env;

use cargo_registry::schema::version_downloads;

fn main() {
    let conn = cargo_registry::db::connect_now().unwrap();
    conn.transaction(|| update(&conn)).unwrap();
}

fn update(conn: &PgConnection) -> QueryResult<()> {
    let ids = env::args().skip(1).filter_map(
        |arg| arg.parse::<i32>().ok(),
    );
    for id in ids {
        let mut rng = StdRng::new().unwrap();
        let mut dls = rng.gen_range(5000i32, 10000);

        for day in 0..90 {
            let moment = Utc::now().date().naive_utc() + Duration::days(-day);
            dls += rng.gen_range(-100, 100);

            let version_download = VersionDownload {
                version_id: id,
                downloads: dls,
                date: moment,
            };
            diesel::insert(&version_download)
                .into(version_downloads::table)
                .execute(conn)?;
        }
    }
    Ok(())
}

#[derive(Insertable)]
#[table_name = "version_downloads"]
struct VersionDownload {
    version_id: i32,
    downloads: i32,
    date: NaiveDate,
}
<|endoftext|>


<commit_before>use url::Url;
use uuid::Uuid;
use hyper::Client;
use hyper::client::RequestBuilder;
use hyper::header::{Headers, ContentType, UserAgent, Authorization};
use serde_json;

use lib::*;
use lib::Result;
use lib::error::*;
use lib::files::*;
use config::{Config, CommandLineOptions};

use std::io::Read;
use std::sync::Arc;

pub struct PasteGg {
  config: Arc<Config>,
  cli: Arc<CommandLineOptions>,
  client: Client
}

impl PasteGg {
  pub fn new(config: Arc<Config>, cli: Arc<CommandLineOptions>) -> PasteGg {
    PasteGg {
      config: config,
      cli: cli,
      client: ::new_client()
    }
  }

  fn add_headers<'a>(&self, rb: RequestBuilder<'a>) -> RequestBuilder<'a> {
    let mut headers = Headers::new();
    headers.set(ContentType::json());
    headers.set(UserAgent(format!("bins/{}", crate_version!())));
    if let Some(true) = self.cli.authed.or(self.config.defaults.authed) {
      if let Some(ref key) = self.config.pastegg.key {
        if !key.is_empty() {
          headers.set(Authorization(format!("Key {}", key)));
        }
      }
    }
    rb.headers(headers)
  }

  fn get_paste(&self, id: &str) -> Result<PasteGgPaste<FullPasteGgFile>> {
    debug!("getting paste for ID {}", id);
    let builder = self.client.get(&format!("https://api.paste.gg/v0/pastes/{}?full=true", id));
    let mut res = self.add_headers(builder).send()?;
    let mut content = String::new();
    res.read_to_string(&mut content)?;
    if res.status.class().default_code() != ::hyper::Ok {
      debug!("bad status code");
      return Err(ErrorKind::InvalidStatus(res.status_raw().0, Some(content)).into());
    }
    let result: PasteGgResult<PasteGgPaste<FullPasteGgFile>> = serde_json::from_str(&content)
      .chain_err(|| "could not parse paste.gg response")?;
    match result {
      PasteGgResult::Success { result } => Ok(result),
      PasteGgResult::Error { error, message } => {
        let mut msg = error;
        if let Some(m) = message {
          msg += ": ";
          msg += &m;
        }
        Err(ErrorKind::BinError(msg).into())
      },
    }
  }
}

impl Bin for PasteGg {
  fn name(&self) -> &str {
    "pastegg"
  }

  fn html_host(&self) -> &str {
    "paste.gg"
  }

  fn raw_host(&self) -> &str {
    "paste.gg"
  }
}

impl ManagesUrls for PasteGg {}

impl CreatesUrls for PasteGg {}

impl FormatsUrls for PasteGg {}

impl FormatsHtmlUrls for PasteGg {
  fn format_html_url(&self, _: &str) -> Option<String> {
    None
  }
}

impl FormatsRawUrls for PasteGg {
  fn format_raw_url(&self, _: &str) -> Option<String> {
    None
  }
}

impl CreatesHtmlUrls for PasteGg {
  fn create_html_url(&self, id: &str) -> Result<Vec<PasteUrl>> {
    let paste = self.get_paste(id)?;
    let urls: Vec<PasteUrl> = paste.files.iter()
      .map(|file| PasteUrl::html(
        file.name.clone().map(PasteFileName::Explicit),
        format!("https://paste.gg/{}", id)
      ))
      .collect();
    Ok(urls)
  }

  fn id_from_html_url(&self, url: &str) -> Option<String> {
    self.id_from_raw_url(url)
  }
}

impl CreatesRawUrls for PasteGg {
  fn create_raw_url(&self, id: &str) -> Result<Vec<PasteUrl>> {
    let paste = self.get_paste(id)?;
    let urls: Vec<PasteUrl> = paste.files.iter()
      .map(|file| PasteUrl::raw(
        file.name.clone().map(PasteFileName::Explicit),
        format!("https://api.paste.gg/v0/pastes/{}/files/{}/raw", paste.id, file.id)
      ))
      .collect();
    Ok(urls)
  }

  fn id_from_raw_url(&self, url: &str) -> Option<String> {
    let mut url = option!(Url::parse(url).ok());
    url.set_fragment(None);
    url.set_query(None);
    option!(url.path_segments())
      .flat_map(|x| Uuid::parse_str(x))
      .map(|x| x.simple().to_string())
      .next()
  }
}

impl HasFeatures for PasteGg {
  fn features(&self) -> Vec<BinFeature> {
    vec![
      BinFeature::Public,
      BinFeature::Private,
      BinFeature::Authed,
      BinFeature::Anonymous,
      BinFeature::MultiFile,
      BinFeature::SingleNaming,
    ]
  }
}

impl Uploads for PasteGg {
  fn upload(&self, contents: &[UploadFile], _: bool) -> Result<Vec<PasteUrl>> {
    let files: Vec<PasteGgUploadFile> = contents
      .iter()
      .map(|file| PasteGgUploadFile {
        name: Some(file.name.clone()),
        content: PasteGgContent::Text(file.content.clone()),
      })
      .collect();
    let visibility = if self.cli.private.or(self.config.defaults.private).map(|x| !x).unwrap_or(false) {
      Visibility::Public
    } else {
      Visibility::Unlisted
    };
    let upload_file = PasteGgUpload {
      name: None,
      description: None,
      visibility,
      files,
    };
    let upload_json = serde_json::to_string(&upload_file)?;
    let builder = self.client.post("https://api.paste.gg/v0/pastes").body(&upload_json);
    let mut res = self.add_headers(builder).send()?;
    let mut content = String::new();
    res.read_to_string(&mut content)?;
    // if res.status != ::hyper::status::StatusCode::Created {
    //   return Err(ErrorKind::BinError(content).into());
    // }
    let paste: PasteGgResult<PasteGgPaste<PartialPasteGgFile>> = serde_json::from_str(&content)?;
    let paste = match paste {
      PasteGgResult::Success { result } => result,
      PasteGgResult::Error { error, message } => {
        let mut msg = error;
        if let Some(m) = message {
          msg += ": ";
          msg += &m;
        }
        return Err(ErrorKind::BinError(msg).into())
      },
    };
    Ok(vec![PasteUrl::html(None, format!("https://paste.gg/{}", paste.id))])
  }
}

impl HasClient for PasteGg {
  fn client(&self) -> &Client {
    &self.client
  }
}

#[derive(Debug, Deserialize)]
#[serde(tag = "status", rename_all = "lowercase")]
enum PasteGgResult<T> {
  Success {
    result: T
  },
  Error {
    error: String,
    message: Option<String>,
  },
}

#[derive(Debug, Deserialize)]
struct PasteGgPaste<T> {
  id: String,
  name: Option<String>,
  description: Option<String>,
  visibility: Visibility,
  files: Vec<T>,
}

#[derive(Debug, Deserialize)]
struct FullPasteGgFile {
  id: String,
  name: Option<String>,
  content: PasteGgContent,
}

#[derive(Debug, Deserialize)]
struct PartialPasteGgFile {
  id: String,
  name: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[serde(tag = "format", content = "value")]
enum PasteGgContent {
  Text(String),
  Base64(String),
  Gzip(String),
  Xz(String),
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Visibility {
  Public,
  Unlisted,
  Private,
}

#[derive(Debug, Serialize)]
struct PasteGgUpload {
  name: Option<String>,
  description: Option<String>,
  visibility: Visibility,
  files: Vec<PasteGgUploadFile>,
}

#[derive(Debug, Serialize)]
struct PasteGgUploadFile {
  name: Option<String>,
  content: PasteGgContent,
}
<commit_msg>feat: paste.gg api to v1 (#47)<commit_after>use url::Url;
use uuid::Uuid;
use hyper::Client;
use hyper::client::RequestBuilder;
use hyper::header::{Headers, ContentType, UserAgent, Authorization};
use serde_json;

use lib::*;
use lib::Result;
use lib::error::*;
use lib::files::*;
use config::{Config, CommandLineOptions};

use std::io::Read;
use std::sync::Arc;

pub struct PasteGg {
  config: Arc<Config>,
  cli: Arc<CommandLineOptions>,
  client: Client
}

impl PasteGg {
  pub fn new(config: Arc<Config>, cli: Arc<CommandLineOptions>) -> PasteGg {
    PasteGg {
      config: config,
      cli: cli,
      client: ::new_client()
    }
  }

  fn add_headers<'a>(&self, rb: RequestBuilder<'a>) -> RequestBuilder<'a> {
    let mut headers = Headers::new();
    headers.set(ContentType::json());
    headers.set(UserAgent(format!("bins/{}", crate_version!())));
    if let Some(true) = self.cli.authed.or(self.config.defaults.authed) {
      if let Some(ref key) = self.config.pastegg.key {
        if !key.is_empty() {
          headers.set(Authorization(format!("Key {}", key)));
        }
      }
    }
    rb.headers(headers)
  }

  fn get_paste(&self, id: &str) -> Result<PasteGgPaste<FullPasteGgFile>> {
    debug!("getting paste for ID {}", id);
    let builder = self.client.get(&format!("https://api.paste.gg/v1/pastes/{}?full=true", id));
    let mut res = self.add_headers(builder).send()?;
    let mut content = String::new();
    res.read_to_string(&mut content)?;
    if res.status.class().default_code() != ::hyper::Ok {
      debug!("bad status code");
      return Err(ErrorKind::InvalidStatus(res.status_raw().0, Some(content)).into());
    }
    let result: PasteGgResult<PasteGgPaste<FullPasteGgFile>> = serde_json::from_str(&content)
      .chain_err(|| "could not parse paste.gg response")?;
    match result {
      PasteGgResult::Success { result } => Ok(result),
      PasteGgResult::Error { error, message } => {
        let mut msg = error;
        if let Some(m) = message {
          msg += ": ";
          msg += &m;
        }
        Err(ErrorKind::BinError(msg).into())
      },
    }
  }
}

impl Bin for PasteGg {
  fn name(&self) -> &str {
    "pastegg"
  }

  fn html_host(&self) -> &str {
    "paste.gg"
  }

  fn raw_host(&self) -> &str {
    "paste.gg"
  }
}

impl ManagesUrls for PasteGg {}

impl CreatesUrls for PasteGg {}

impl FormatsUrls for PasteGg {}

impl FormatsHtmlUrls for PasteGg {
  fn format_html_url(&self, _: &str) -> Option<String> {
    None
  }
}

impl FormatsRawUrls for PasteGg {
  fn format_raw_url(&self, _: &str) -> Option<String> {
    None
  }
}

impl CreatesHtmlUrls for PasteGg {
  fn create_html_url(&self, id: &str) -> Result<Vec<PasteUrl>> {
    let paste = self.get_paste(id)?;
    let urls: Vec<PasteUrl> = paste.files.iter()
      .map(|file| PasteUrl::html(
        file.name.clone().map(PasteFileName::Explicit),
        format!("https://paste.gg/{}", id)
      ))
      .collect();
    Ok(urls)
  }

  fn id_from_html_url(&self, url: &str) -> Option<String> {
    self.id_from_raw_url(url)
  }
}

impl CreatesRawUrls for PasteGg {
  fn create_raw_url(&self, id: &str) -> Result<Vec<PasteUrl>> {
    let paste = self.get_paste(id)?;
    let urls: Vec<PasteUrl> = paste.files.iter()
      .map(|file| PasteUrl::raw(
        file.name.clone().map(PasteFileName::Explicit),
        format!("https://api.paste.gg/v1/pastes/{}/files/{}/raw", paste.id, file.id)
      ))
      .collect();
    Ok(urls)
  }

  fn id_from_raw_url(&self, url: &str) -> Option<String> {
    let mut url = option!(Url::parse(url).ok());
    url.set_fragment(None);
    url.set_query(None);
    option!(url.path_segments())
      .flat_map(|x| Uuid::parse_str(x))
      .map(|x| x.simple().to_string())
      .next()
  }
}

impl HasFeatures for PasteGg {
  fn features(&self) -> Vec<BinFeature> {
    vec![
      BinFeature::Public,
      BinFeature::Private,
      BinFeature::Authed,
      BinFeature::Anonymous,
      BinFeature::MultiFile,
      BinFeature::SingleNaming,
    ]
  }
}

impl Uploads for PasteGg {
  fn upload(&self, contents: &[UploadFile], _: bool) -> Result<Vec<PasteUrl>> {
    let files: Vec<PasteGgUploadFile> = contents
      .iter()
      .map(|file| PasteGgUploadFile {
        name: Some(file.name.clone()),
        content: PasteGgContent::Text(file.content.clone()),
      })
      .collect();
    let visibility = if self.cli.private.or(self.config.defaults.private).map(|x| !x).unwrap_or(false) {
      Visibility::Public
    } else {
      Visibility::Unlisted
    };
    let upload_file = PasteGgUpload {
      name: None,
      description: None,
      visibility,
      files,
    };
    let upload_json = serde_json::to_string(&upload_file)?;
    let builder = self.client.post("https://api.paste.gg/v1/pastes").body(&upload_json);
    let mut res = self.add_headers(builder).send()?;
    let mut content = String::new();
    res.read_to_string(&mut content)?;
    // if res.status != ::hyper::status::StatusCode::Created {
    //   return Err(ErrorKind::BinError(content).into());
    // }
    let paste: PasteGgResult<PasteGgPaste<PartialPasteGgFile>> = serde_json::from_str(&content)?;
    let paste = match paste {
      PasteGgResult::Success { result } => result,
      PasteGgResult::Error { error, message } => {
        let mut msg = error;
        if let Some(m) = message {
          msg += ": ";
          msg += &m;
        }
        return Err(ErrorKind::BinError(msg).into())
      },
    };
    Ok(vec![PasteUrl::html(None, format!("https://paste.gg/{}", paste.id))])
  }
}

impl HasClient for PasteGg {
  fn client(&self) -> &Client {
    &self.client
  }
}

#[derive(Debug, Deserialize)]
#[serde(tag = "status", rename_all = "lowercase")]
enum PasteGgResult<T> {
  Success {
    result: T
  },
  Error {
    error: String,
    message: Option<String>,
  },
}

#[derive(Debug, Deserialize)]
struct PasteGgPaste<T> {
  id: String,
  name: Option<String>,
  description: Option<String>,
  visibility: Visibility,
  files: Vec<T>,
}

#[derive(Debug, Deserialize)]
struct FullPasteGgFile {
  id: String,
  name: Option<String>,
  content: PasteGgContent,
}

#[derive(Debug, Deserialize)]
struct PartialPasteGgFile {
  id: String,
  name: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[serde(tag = "format", content = "value")]
enum PasteGgContent {
  Text(String),
  Base64(String),
  Gzip(String),
  Xz(String),
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Visibility {
  Public,
  Unlisted,
  Private,
}

#[derive(Debug, Serialize)]
struct PasteGgUpload {
  name: Option<String>,
  description: Option<String>,
  visibility: Visibility,
  files: Vec<PasteGgUploadFile>,
}

#[derive(Debug, Serialize)]
struct PasteGgUploadFile {
  name: Option<String>,
  content: PasteGgContent,
}
<|endoftext|>


<commit_before>#![crate_name="fibonacci"]
#![crate_type = "lib"]
#![cfg_attr(feature = "nightly", feature(test))]

//! Compute fibonacci.
//!
//! The Fibonacci sequence is a sequence of numbers starting with 0 and 1 where every subsequent number is the sum of the previous two numbers.
//!
//! Tested with rust-1.41.0 and rust-1.41.0-nightly
//! Nightly is needed to run benchmarks.
//! https://doc.rust-lang.org/unstable-book/library-features/test.html
//!
//! To run the benchmark, use nightly version and
//! `cargo bench --bin fibonacci --features=nightly`
//!
//! @license MIT license <http://www.opensource.org/licenses/mit-license.php>
//!
//! @author Eliovir <http://github.com/~eliovir>

/**
* Calcule les elements de la suite de Fibonnaci.
*
* REGLE rg_024 Le projet permet de calculer les membres de la Suite de
* Fibonacci.
*
* REGLE rg_024.1 : f(1) = 1
* REGLE rg_024.2 : f(2) = 1
*
* REGLE rg_024.3 : f(n) = f(n-1) + f(n-2) si n > 2
*
* REGLE rg_024.4 : il n'est pas possible de calculer la valeur de la Suite
* de Fibonacci pour un rang negatif.
*
* REGLE rg_024.5 : le calcul de n'importe quel element de la Suite de
* Fibonacci doit s'effectuer en moins de deux secondes.
*
* REGLE rg_024.6 : le calcul de n'importe quel element de la Suite de
* Fibonacci, pour un rang inferieur a 50, doit s'effectuer en moins d'une
* seconde.
*
* @param n le rang pour lequel on calcule le membre.
* @return Le membre de rang n dans la Suite.
*/
pub fn fibonacci_reccursive(n: i32) -> u64 {
	if n < 0 {
		panic!("{} is negative!", n);
	}
	match n {
		0     => panic!("zero is not a right argument to fibonacci_reccursive()!"),
		1 | 2 => 1,
		3     => 2,
		/*
		50    => 12586269025,
		*/
		_     => fibonacci_reccursive(n - 1) + fibonacci_reccursive(n - 2)
	}
}

/**
 * Non reccursive function.
 */
pub fn fibonacci(n: i32) -> u64 {
	if n < 0 {
		panic!("{} is negative!", n);
	} else if n == 0 {
		panic!("zero is not a right argument to fibonacci()!");
	} else if n == 1 {
		return 1;
	}

	let mut sum = 0;
	let mut last = 0;
	let mut curr = 1;
	for _i in 1..n {
		sum = last + curr;
		last = curr;
		curr = sum;
	}
	sum
}

/**
 * Iterative fibonacci
 * https://github.com/rust-lang/rust-by-example
 */
pub struct Fibonacci {
	curr: u64,
	next: u64,
}

impl Iterator for Fibonacci {
	type Item = u64;
	fn next(&mut self) -> Option<u64> {
		let new_next = self.curr + self.next;

		self.curr = self.next;
		self.next = new_next;

		Some(self.curr)
	}
}

/**
 * A "constructor" for Iiterative fibonacci
 */
pub fn iterative_fibonacci() -> Fibonacci {
	Fibonacci { curr: 1, next: 1 }
}

#[cfg(test)]
fn rg_024_x(n: i32, expected: u64) {
	let mut found = fibonacci_reccursive(n);
	assert!(expected == found, format!("fibibonacci_reccursive({}): expected ({}) != found ({})", n, expected, found));
	found = fibonacci(n);
	assert!(expected == found, format!("fibibonacci({}): expected ({}) != found ({})", n, expected, found));
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.1 : f(1) = 1
 */
#[test]
fn rg_024_1() {
	rg_024_x(1, 1);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.2 : f(2) = 1
 */
#[test]
fn rg_024_2() {
	rg_024_x(2, 1);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.3.a : f(3) = 2
 */
#[test]
fn rg_024_3_a() {
	rg_024_x(3, 2);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.4.a : il n'est pas possible de calculer la valeur de la Suite
 * de Fibonacci pour un rang negatif ou nul.
 */
#[test]
#[should_panic]
fn rg_024_4_a() {
	fibonacci(-1);
	fibonacci(0);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.5 : f(55) = 139583862445
 */
#[test]
fn rg_024_5() {
	/*rg_024_x(55, 139583862445);*/
	rg_024_x(30, 832040);
}

#[cfg(feature="nightly")]
#[cfg(test)]
mod bench {
	extern crate test;
    use self::test::Bencher;
    use super::*;

    // bench: find the `BENCH_SIZE` first terms of the fibonacci sequence
    #[cfg(test)]
    static BENCH_SIZE: usize = 20;
    #[cfg(test)]
    static BENCH_SIZE_INT: i32 = 20;

    #[bench]
    fn bench_fibonacci_reccursive(b: &mut Bencher) {
        b.iter(|| {
            fibonacci_reccursive(BENCH_SIZE_INT);
        });
    }
    #[bench]
    fn bench_fibonacci(b: &mut Bencher) {
        b.iter(|| {
            fibonacci(BENCH_SIZE_INT);
        });
    }
    #[bench]
    fn bench_iterative_fibonacci(b: &mut Bencher) {
        b.iter(|| {
            // http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take
            iterative_fibonacci().take(BENCH_SIZE).last().unwrap()
            //iterative_fibonacci().take(BENCH_SIZE).collect::<Vec<u64>>()
        })
    }

}
<commit_msg>Add comments. fibonacci_reccursive is not for real use case. fixes #7<commit_after>#![crate_name="fibonacci"]
#![crate_type = "lib"]
#![cfg_attr(feature = "nightly", feature(test))]

//! Compute fibonacci.
//!
//! The Fibonacci sequence is a sequence of numbers starting with 0 and 1 where every subsequent number is the sum of the previous two numbers.
//!
//! Tested with rust-1.41.0 and rust-1.41.0-nightly
//! Nightly is needed to run benchmarks.
//! https://doc.rust-lang.org/unstable-book/library-features/test.html
//!
//! To run the benchmark, use nightly version and
//! `cargo bench --bin fibonacci --features=nightly`
//!
//! @license MIT license <http://www.opensource.org/licenses/mit-license.php>
//!
//! @author Eliovir <http://github.com/~eliovir>

/*
* Calcule les elements de la suite de Fibonnaci.
*
* REGLE rg_024 Le projet permet de calculer les membres de la Suite de
* Fibonacci.
*
* REGLE rg_024.1 : f(1) = 1
* REGLE rg_024.2 : f(2) = 1
*
* REGLE rg_024.3 : f(n) = f(n-1) + f(n-2) si n > 2
*
* REGLE rg_024.4 : il n'est pas possible de calculer la valeur de la Suite
* de Fibonacci pour un rang negatif.
*
* REGLE rg_024.5 : le calcul de n'importe quel element de la Suite de
* Fibonacci doit s'effectuer en moins de deux secondes.
*
* REGLE rg_024.6 : le calcul de n'importe quel element de la Suite de
* Fibonacci, pour un rang inferieur a 50, doit s'effectuer en moins d'une
* seconde.
*/

/// This code shows the wrong way to compute the Fibonacci sequence, the complexity is exponential.
///
/// It's here to play with `#[bench]`.
///
/// `n` the rank used to compute the member of the sequence.
pub fn fibonacci_reccursive(n: i32) -> u64 {
	if n < 0 {
		panic!("{} is negative!", n);
	}
	match n {
		0     => panic!("zero is not a right argument to fibonacci_reccursive()!"),
		1 | 2 => 1,
		3     => 2,
		/*
		50    => 12586269025,
		*/
		_     => fibonacci_reccursive(n - 1) + fibonacci_reccursive(n - 2)
	}
}

/// Non reccursive function.
///
/// `n` the rank used to compute the member of the sequence.
pub fn fibonacci(n: i32) -> u64 {
	if n < 0 {
		panic!("{} is negative!", n);
	} else if n == 0 {
		panic!("zero is not a right argument to fibonacci()!");
	} else if n == 1 {
		return 1;
	}

	let mut sum = 0;
	let mut last = 0;
	let mut curr = 1;
	for _i in 1..n {
		sum = last + curr;
		last = curr;
		curr = sum;
	}
	sum
}

/// Iterative fibonacci.
///
/// https://github.com/rust-lang/rust-by-example
pub struct Fibonacci {
	curr: u64,
	next: u64,
}

impl Iterator for Fibonacci {
	type Item = u64;
	fn next(&mut self) -> Option<u64> {
		let new_next = self.curr + self.next;

		self.curr = self.next;
		self.next = new_next;

		Some(self.curr)
	}
}

/// A "constructor" for Iterative fibonacci.
pub fn iterative_fibonacci() -> Fibonacci {
	Fibonacci { curr: 1, next: 1 }
}

#[cfg(test)]
fn rg_024_x(n: i32, expected: u64) {
	let mut found = fibonacci_reccursive(n);
	assert!(expected == found, format!("fibibonacci_reccursive({}): expected ({}) != found ({})", n, expected, found));
	found = fibonacci(n);
	assert!(expected == found, format!("fibibonacci({}): expected ({}) != found ({})", n, expected, found));
}

/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.1 : f(1) = 1
 */
#[test]
fn rg_024_1() {
	rg_024_x(1, 1);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.2 : f(2) = 1
 */
#[test]
fn rg_024_2() {
	rg_024_x(2, 1);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.3.a : f(3) = 2
 */
#[test]
fn rg_024_3_a() {
	rg_024_x(3, 2);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.4.a : il n'est pas possible de calculer la valeur de la Suite
 * de Fibonacci pour un rang negatif ou nul.
 */
#[test]
#[should_panic]
fn rg_024_4_a() {
	fibonacci(-1);
	fibonacci(0);
}
/**
 * Test du calcul de la suite de Fibonnaci.
 *
 * REGLE rg_024.5 : f(55) = 139583862445
 */
#[test]
fn rg_024_5() {
	/*rg_024_x(55, 139583862445);*/
	rg_024_x(30, 832040);
}

#[cfg(feature="nightly")]
#[cfg(test)]
mod bench {
	extern crate test;
    use self::test::Bencher;
    use super::*;

    // bench: find the `BENCH_SIZE` first terms of the fibonacci sequence
    #[cfg(test)]
    static BENCH_SIZE: usize = 20;
    #[cfg(test)]
    static BENCH_SIZE_INT: i32 = 20;

    #[bench]
    fn bench_fibonacci_reccursive(b: &mut Bencher) {
        b.iter(|| {
            fibonacci_reccursive(BENCH_SIZE_INT);
        });
    }
    #[bench]
    fn bench_fibonacci(b: &mut Bencher) {
        b.iter(|| {
            fibonacci(BENCH_SIZE_INT);
        });
    }
    #[bench]
    fn bench_iterative_fibonacci(b: &mut Bencher) {
        b.iter(|| {
            // http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take
            iterative_fibonacci().take(BENCH_SIZE).last().unwrap()
            //iterative_fibonacci().take(BENCH_SIZE).collect::<Vec<u64>>()
        })
    }

}
<|endoftext|>


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

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MultiArchiveJsonWriter {
    archives: HashMap<String, ZipArchiveJsonWriter>,
}

impl MultiArchiveJsonWriter {
    pub fn new() -> MultiArchiveJsonWriter {
        MultiArchiveJsonWriter { archives: HashMap::new() }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipArchiveJsonWriter {
    objects: HashMap<String, ZipObjectJsonWriter>,
}

impl ZipArchiveJsonWriter {
    /// Construct ZipArchiveJsonWriter with empty map of objects:
    pub fn new() -> ZipArchiveJsonWriter {
        ZipArchiveJsonWriter { objects: HashMap::new() }
    }

    /// Create and fill ZipArchiveJsonWriter representing a
    /// .zip file:
    pub fn from(file_path: &str) -> ZipArchiveJsonWriter {
        //let whole_archive = zip::ZipArchive::new(file_path).unwrap();
        // Default result:
        ZipArchiveJsonWriter::new()
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipObjectJsonWriter {
    compression_type: String,
    original_size: u64,
    compressed_size: u64,
    compression_rate: String,
}

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

    fn get_zip_object() -> ZipObjectJsonWriter {
        ZipObjectJsonWriter {
            compression_type: format!("{}", zip::CompressionMethod::Deflated),
            original_size: 100,
            compressed_size: 50,
            compression_rate: String::from("50%"),
        }
    }

    fn get_zip_archive() -> ZipArchiveJsonWriter {
        let mut zip_archive = ZipArchiveJsonWriter::new();
        zip_archive.objects.insert(String::from("foo.txt"), get_zip_object());
        zip_archive
    }

    #[test]
    fn test_serialize_object_json_writer() {
        let zip_object = get_zip_object();

        let zip_object_serialized = serde_json::to_string(&zip_object)
            .unwrap();

        let zip_object_pretty = serde_json::
            to_string_pretty(&zip_object).unwrap();

        let zip_object_deserialized: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_serialized.as_str()).unwrap();

        let zip_object_depretty: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_pretty.as_str()).unwrap();

        assert_eq!(zip_object, zip_object_deserialized);
        assert_eq!(zip_object, zip_object_depretty);
    }

    #[test]
    fn test_new_archive_has_empty_map_of_zip_objects() {
        let zip_archive = ZipArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipObjectJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, zip_archive.objects);
    }

    #[test]
    fn test_serialize_archive_json_writer() {
        let zip_archive = get_zip_archive();

        let zip_archive_serialized = serde_json::to_string(&zip_archive)
            .unwrap();

        let zip_archive_pretty = serde_json::to_string_pretty(&zip_archive)
            .unwrap();

        let zip_archive_deserialized: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_serialized.as_str()).unwrap();

        let zip_archive_depretty: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_pretty.as_str()).unwrap();

        assert_eq!(zip_archive, zip_archive_deserialized);
        assert_eq!(zip_archive, zip_archive_depretty);
    }

    #[test]
    fn test_new_multi_archive_has_empty_map_of_zip_archives() {
        let multi_archive = MultiArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipArchiveJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, multi_archive.archives);
    }

    #[test]
    fn test_serialize_multi_archive_json_writer() {
        let zip_archive = get_zip_archive();
        let mut multi_archive = MultiArchiveJsonWriter::new();

        multi_archive.archives.insert(String::from("bar.zip"), zip_archive);

        let multi_archive_serialized = serde_json::to_string(&multi_archive)
            .unwrap();

        let multi_archive_pretty = serde_json::to_string_pretty(&multi_archive)
            .unwrap();

        let multi_archive_deserialized: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_serialized.as_str()).unwrap();

        let multi_archive_depretty: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_pretty.as_str()).unwrap();

        assert_eq!(multi_archive, multi_archive_deserialized);
        assert_eq!(multi_archive, multi_archive_depretty);
    }
}
<commit_msg>Add ZipArchiveJsonWriter::from<commit_after>use ::glob;
use ::serde_json;
use ::zip;
use std::collections::HashMap;
use std::fs;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct MultiArchiveJsonWriter {
    archives: HashMap<String, ZipArchiveJsonWriter>,
}

impl MultiArchiveJsonWriter {
    pub fn new() -> MultiArchiveJsonWriter {
        MultiArchiveJsonWriter { archives: HashMap::new() }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipArchiveJsonWriter {
    objects: HashMap<String, ZipObjectJsonWriter>,
}

impl ZipArchiveJsonWriter {
    /// Construct ZipArchiveJsonWriter with empty map of objects:
    pub fn new() -> ZipArchiveJsonWriter {
        ZipArchiveJsonWriter { objects: HashMap::new() }
    }

    /// Create and fill ZipArchiveJsonWriter representing a
    /// .zip file:
    pub fn from(file_path: &str, exclude: &str) -> ZipArchiveJsonWriter {
        let mut archive_writer = ZipArchiveJsonWriter::new();
        let exclude_pattern = glob::Pattern::new(exclude).unwrap();

        let file = fs::File::open(file_path).unwrap();
        let mut whole_archive = zip::ZipArchive::new(file).unwrap();

        // Loop through the objects within the archive (.zip file)...
        for i in 0..whole_archive.len() {
            let zip_object = whole_archive.by_index(i).unwrap();

            // ...and if it isn't excluded, add the object to
            // the HashMap in the archive writer:
            if !exclude_pattern.matches(zip_object.name()) {
                archive_writer.objects.insert(
                    String::from(zip_object.name()),
                    ZipObjectJsonWriter {
                        compression_type: format!("{}", zip_object.compression()),
                        original_size: zip_object.size(),
                        compressed_size: zip_object.compressed_size(),
                        compression_rate: String::from("0%"),
                    }
                );
            }
        }

        archive_writer
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct ZipObjectJsonWriter {
    compression_type: String,
    original_size: u64,
    compressed_size: u64,
    compression_rate: String,
}

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

    fn get_zip_object() -> ZipObjectJsonWriter {
        ZipObjectJsonWriter {
            compression_type: format!("{}", zip::CompressionMethod::Deflated),
            original_size: 100,
            compressed_size: 50,
            compression_rate: String::from("50%"),
        }
    }

    fn get_zip_archive() -> ZipArchiveJsonWriter {
        let mut zip_archive = ZipArchiveJsonWriter::new();
        zip_archive.objects.insert(String::from("foo.txt"), get_zip_object());
        zip_archive
    }

    #[test]
    fn test_serialize_object_json_writer() {
        let zip_object = get_zip_object();

        let zip_object_serialized = serde_json::to_string(&zip_object)
            .unwrap();

        let zip_object_pretty = serde_json::
            to_string_pretty(&zip_object).unwrap();

        let zip_object_deserialized: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_serialized.as_str()).unwrap();

        let zip_object_depretty: ZipObjectJsonWriter =
            serde_json::from_str(zip_object_pretty.as_str()).unwrap();

        assert_eq!(zip_object, zip_object_deserialized);
        assert_eq!(zip_object, zip_object_depretty);
    }

    #[test]
    fn test_new_archive_has_empty_map_of_zip_objects() {
        let zip_archive = ZipArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipObjectJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, zip_archive.objects);
    }

    #[test]
    fn test_serialize_archive_json_writer() {
        let zip_archive = get_zip_archive();

        let zip_archive_serialized = serde_json::to_string(&zip_archive)
            .unwrap();

        let zip_archive_pretty = serde_json::to_string_pretty(&zip_archive)
            .unwrap();

        let zip_archive_deserialized: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_serialized.as_str()).unwrap();

        let zip_archive_depretty: ZipArchiveJsonWriter =
            serde_json::from_str(zip_archive_pretty.as_str()).unwrap();

        assert_eq!(zip_archive, zip_archive_deserialized);
        assert_eq!(zip_archive, zip_archive_depretty);
    }

    #[test]
    fn test_new_multi_archive_has_empty_map_of_zip_archives() {
        let multi_archive = MultiArchiveJsonWriter::new();
        let empty_hashmap: HashMap<String, ZipArchiveJsonWriter> =
            HashMap::new();

        assert_eq!(empty_hashmap, multi_archive.archives);
    }

    #[test]
    fn test_serialize_multi_archive_json_writer() {
        let zip_archive = get_zip_archive();
        let mut multi_archive = MultiArchiveJsonWriter::new();

        multi_archive.archives.insert(String::from("bar.zip"), zip_archive);

        let multi_archive_serialized = serde_json::to_string(&multi_archive)
            .unwrap();

        let multi_archive_pretty = serde_json::to_string_pretty(&multi_archive)
            .unwrap();

        let multi_archive_deserialized: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_serialized.as_str()).unwrap();

        let multi_archive_depretty: MultiArchiveJsonWriter =
            serde_json::from_str(multi_archive_pretty.as_str()).unwrap();

        assert_eq!(multi_archive, multi_archive_deserialized);
        assert_eq!(multi_archive, multi_archive_depretty);
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashSet};

/// Enumeration of possible channel modes
/// as of http://tools.ietf.org/html/rfc2811#section-4
#[deriving(FromPrimitive, Show, Clone, Hash, PartialEq, Eq)]
pub enum ChannelMode {
    /// give "channel creator" status
    ChannelCreator = b'O' as int,
    /// give/take channel operator privilege
    OperatorPrivilege = b'o' as int,
    /// give/take the voice privilege
    VoicePrivilege = b'v' as int,
    /// toggle the anonymous channel flag
    AnonChannel = b'a' as int,
    /// toggle the invite-only channel flag
    InviteOnly = b'i' as int,
    /// toggle the moderated channel
    Moderated = b'm' as int,
    /// toggle the no messages to channel from clients on the
    /// outside
    MemberOnly = b'n' as int,
    /// toggle the quiet channel flag
    Quiet = b'q' as int,
    /// toggle the private channel flag
    Private = b'p' as int,
    /// toggle the secret channel flag
    Secret = b's' as int,
    /// toggle the server reop channel flag
    ReOpFlag = b'r' as int,
    /// toggle the topic settable by channel operator only flag
    TopicProtect = b't' as int,
    /// set/remove the channel key (password)
    ChannelKey = b'k' as int,
    /// set/remove the user limit to channel
    UserLimit = b'l' as int,
    /// set/remove ban mask to keep users out
    BanMask = b'b' as int,
    /// set/remove an exception mask to override a ban mask
    ExceptionMask = b'e' as int,
    /// set/remove an invitation mask to automatically override
    /// the invite-only flag
    InvitationMask = b'I' as int
}

// Actions which determine what to do with a mode
#[deriving(PartialEq, Eq, Show)]
pub enum Action {
    // Add a flag
    Add,
    // Remove a flag
    Remove,
    // Show the flag
    Show
}

impl ChannelMode {
    fn has_parameter(&self) -> bool {
        match *self {
            ChannelKey | UserLimit | BanMask
            | ExceptionMask | InvitationMask
            | OperatorPrivilege | VoicePrivilege => true,
            _ => false
        }
    }
}

/// Parses the channel modes
///
/// According to [RFC 2812] (http://tools.ietf.org/html/rfc2812#section-3.2.3) the
/// syntax for setting modes is:
/// ```
///    Command: MODE
/// Parameters: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
/// ```
///
/// Additionally an example is given
///
/// ```
/// MODE &oulu +b *!*@*.edu +e *!*@*.bu.edu
///                                 ; Command to prevent any user from a
///                                 hostname matching *.edu from joining,
///                                 except if matching *.bu.edu
///
/// MODE #bu +be *!*@*.edu *!*@*.bu.edu
///                                 ; Comment to prevent any user from a
///                                 hostname matching *.edu from joining,
///                                 except if matching *.bu.edu
/// ```
///
///
pub fn modes_do(slice: &[&[u8]], block: |Action, ChannelMode, Option<&[u8]>|) {
    let mut current = slice;
    loop {
        // Bug: no +/- asking for modes
        let (action, offset) = match current[0][0] {
            b'+' => (Add, 1),
            b'-' => (Remove, 1),
            _ => (Show, 0)

        };
        for mode in current[0].slice_from(offset).iter().filter_map( |&v| {
            let m: Option<ChannelMode> = FromPrimitive::from_u8(v); m
        }) {
            let param = if mode.has_parameter() && action != Show {
                let param = current.get(1).map(|v| *v);
                if current.len() > 1 {
                    current = current.slice_from(1);
                } else { current = &[]; }
                param
            } else {
                None
            };
            block(action, mode, param);
        }
        if current.len() > 1 {
            current = current.slice_from(1);
        } else { break }
    }
}

/// List of channel modes / member flags
pub type Flags = HashSet<ChannelMode>;

#[cfg(test)]
mod tests {
	use super::{modes_do, OperatorPrivilege, BanMask, ExceptionMask, Add, Remove, Show};
    use msg::{RawMessage};
	/// Tests the mode parser

	#[test]
	fn test_mode_parser() {
        let msgs = [
            b"MODE &oulu +b *!*@*.edu +e *!*@*.bu.edu",
            b"MODE #bu +be *!*@*.edu *!*@*.bu.edu",
            b"MODE #bu b",
            b"MODE #test -oo Guest",
            //b"MODE #bu /i", // Invalid mode should be skipped
            b"MODE #bu +g", // Invalid mode should be skipped
        ];
        let modes = [
            vec![(Add, BanMask, Some(b"*!*@*.edu")),
            (Add, ExceptionMask, Some(b"*!*@*.bu.edu"))],
            vec![(Add, BanMask, Some(b"*!*@*.edu")),
            (Add, ExceptionMask, Some(b"*!*@*.bu.edu"))],
            vec![(Show, BanMask, None)],
            vec![(Remove, OperatorPrivilege, Some(b"Guest")),
            (Remove, OperatorPrivilege, None)],
            //Vec::new(),
            Vec::new(),
        ];
        for (msg, modes) in msgs.iter().zip(modes.iter()) {
            let m = RawMessage::parse(*msg).unwrap();
            let mut mode_iter = modes.iter();
            modes_do(m.params().slice_from(1), |set, mode, parameter| {
                let (set_, mode_, parameter_) = *mode_iter.next().unwrap();
                assert_eq!(set_, set);
                assert_eq!(mode_, mode);
                assert_eq!(parameter_.to_string(), parameter.to_string());
            })
        }
	}
}<commit_msg>update to latest rust<commit_after>use std::collections::{HashSet};

/// Enumeration of possible channel modes
/// as of http://tools.ietf.org/html/rfc2811#section-4
#[deriving(FromPrimitive, Show, Clone, Hash, PartialEq, Eq)]
pub enum ChannelMode {
    /// give "channel creator" status
    ChannelCreator = b'O' as int,
    /// give/take channel operator privilege
    OperatorPrivilege = b'o' as int,
    /// give/take the voice privilege
    VoicePrivilege = b'v' as int,
    /// toggle the anonymous channel flag
    AnonChannel = b'a' as int,
    /// toggle the invite-only channel flag
    InviteOnly = b'i' as int,
    /// toggle the moderated channel
    Moderated = b'm' as int,
    /// toggle the no messages to channel from clients on the
    /// outside
    MemberOnly = b'n' as int,
    /// toggle the quiet channel flag
    Quiet = b'q' as int,
    /// toggle the private channel flag
    Private = b'p' as int,
    /// toggle the secret channel flag
    Secret = b's' as int,
    /// toggle the server reop channel flag
    ReOpFlag = b'r' as int,
    /// toggle the topic settable by channel operator only flag
    TopicProtect = b't' as int,
    /// set/remove the channel key (password)
    ChannelKey = b'k' as int,
    /// set/remove the user limit to channel
    UserLimit = b'l' as int,
    /// set/remove ban mask to keep users out
    BanMask = b'b' as int,
    /// set/remove an exception mask to override a ban mask
    ExceptionMask = b'e' as int,
    /// set/remove an invitation mask to automatically override
    /// the invite-only flag
    InvitationMask = b'I' as int
}

// Actions which determine what to do with a mode
#[deriving(PartialEq, Eq, Show)]
pub enum Action {
    // Add a flag
    Add,
    // Remove a flag
    Remove,
    // Show the flag
    Show
}

impl ChannelMode {
    fn has_parameter(&self) -> bool {
        match *self {
            ChannelKey | UserLimit | BanMask
            | ExceptionMask | InvitationMask
            | OperatorPrivilege | VoicePrivilege => true,
            _ => false
        }
    }
}

/// Parses the channel modes
///
/// According to [RFC 2812] (http://tools.ietf.org/html/rfc2812#section-3.2.3) the
/// syntax for setting modes is:
/// ```
///    Command: MODE
/// Parameters: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
/// ```
///
/// Additionally an example is given
///
/// ```
/// MODE &oulu +b *!*@*.edu +e *!*@*.bu.edu
///                                 ; Command to prevent any user from a
///                                 hostname matching *.edu from joining,
///                                 except if matching *.bu.edu
///
/// MODE #bu +be *!*@*.edu *!*@*.bu.edu
///                                 ; Comment to prevent any user from a
///                                 hostname matching *.edu from joining,
///                                 except if matching *.bu.edu
/// ```
///
///
pub fn modes_do(slice: &[&[u8]], block: |Action, ChannelMode, Option<&[u8]>|) {
    let mut current = slice;
    loop {
        // Bug: no +/- asking for modes
        let (action, offset) = match current[0][0] {
            b'+' => (Add, 1),
            b'-' => (Remove, 1),
            _ => (Show, 0)

        };
        for mode in current[0].slice_from(offset).iter().filter_map( |&v| {
            let m: Option<ChannelMode> = FromPrimitive::from_u8(v); m
        }) {
            let param = if mode.has_parameter() && action != Show {
                let param = current.get(1).map(|v| *v);
                if current.len() > 1 {
                    current = current.slice_from(1);
                } else { current = [].as_slice(); }
                param
            } else {
                None
            };
            block(action, mode, param);
        }
        if current.len() > 1 {
            current = current.slice_from(1);
        } else { break }
    }
}

/// List of channel modes / member flags
pub type Flags = HashSet<ChannelMode>;

#[cfg(test)]
mod tests {
	use super::{modes_do, OperatorPrivilege, BanMask, ExceptionMask, Add, Remove, Show};
    use msg::{RawMessage};
	/// Tests the mode parser

	#[test]
	fn test_mode_parser() {
        let msgs = [
            b"MODE &oulu +b *!*@*.edu +e *!*@*.bu.edu",
            b"MODE #bu +be *!*@*.edu *!*@*.bu.edu",
            b"MODE #bu b",
            b"MODE #test -oo Guest",
            //b"MODE #bu /i", // Invalid mode should be skipped
            b"MODE #bu +g", // Invalid mode should be skipped
        ];
        let modes = [
            vec![(Add, BanMask, Some(b"*!*@*.edu")),
            (Add, ExceptionMask, Some(b"*!*@*.bu.edu"))],
            vec![(Add, BanMask, Some(b"*!*@*.edu")),
            (Add, ExceptionMask, Some(b"*!*@*.bu.edu"))],
            vec![(Show, BanMask, None)],
            vec![(Remove, OperatorPrivilege, Some(b"Guest")),
            (Remove, OperatorPrivilege, None)],
            //Vec::new(),
            Vec::new(),
        ];
        for (msg, modes) in msgs.iter().zip(modes.iter()) {
            let m = RawMessage::parse(*msg).unwrap();
            let mut mode_iter = modes.iter();
            modes_do(m.params().slice_from(1), |set, mode, parameter| {
                let (set_, mode_, parameter_) = *mode_iter.next().unwrap();
                assert_eq!(set_, set);
                assert_eq!(mode_, mode);
                assert_eq!(parameter_.to_string(), parameter.to_string());
            })
        }
	}
}<|endoftext|>