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

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) {
    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> bool {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => true,
        (Type::Boolean, Type::Boolean) => true,

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => {
            bind_type(env, n, ty);
            true
        }

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() { return false }

            for iter in args1.iter().zip(args2.iter()) {
                let (a1, a2) = iter;

                if !unify(a1.clone(), a2.clone(), env) { return false }
            }

            unify(*body1, *body2, env)
        }

        _ => false
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let body_ty = w(body, env, inst);
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        // TODO: Need unification algo here.
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            let lambda_ty = w(callable, env, inst);

            match lambda_ty {
                Type::Lambda(expected, body_type) => {
                    if args.len() != expected.len() {
                        panic!("wrong number of args given, {} for {}", args.len(), expected.len())
                    } else if expected != type_args.as_slice() {
                        panic!("bad types given: {:?} for {:?}", type_args.as_slice(), expected)
                    }

                    *body_type.clone()
                }

                _ => panic!("trying to call non-function")
            }
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);
        // let var = Expression::Variable("x");

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);

        // FIXME: until we return a result type this will panic.
        // assert_eq!(w(&var, &mut env, &mut inst), Type::Unbound(1));
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(2)], Box::new(
                       Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    // TODO: test the other inferencers

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert!(unify(Type::Number, Type::Number, &mut env));
        assert_eq!(unify(Type::Number, Type::Boolean, &mut env), false);
        assert!(unify(Type::Unbound(1), Type::Number, &mut env));

        assert!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                      Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                      &mut env));
    }
}

fn main() {
    println!("patience.");
}
<commit_msg>Add unification call to FnCall.<commit_after>use std::fmt::Display;
use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> bool {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => true,
        (Type::Boolean, Type::Boolean) => true,

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => {
            bind_type(env, n, ty);
            true
        }

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() { return false }

            for iter in args1.iter().zip(args2.iter()) {
                let (a1, a2) = iter;

                if !unify(a1.clone(), a2.clone(), env) { return false }
            }

            unify(*body1, *body2, env)
        }

        _ => false
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let body_ty = w(body, env, inst);
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        // TODO: Need unification algo here.
        Expression::FnCall(callable, args) => {
            // FIXME: this seems broken.
            let mut env_ = env.clone();
            let mut inst_ = inst.clone();

            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, &mut env_, &mut inst_)
                })
                .collect::<Vec<Type>>();

            let return_type = Type::Unbound(inst.next());

            let lambda_ty = w(callable, env, inst);
            let derived_ty = Type::Lambda(type_args, Box::new(return_type.clone()));

            if !unify(lambda_ty.clone(), derived_ty.clone(), env) {
                panic!("could not unify! {:?} for {:?}", lambda_ty, derived_ty)
            }

            return_type
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);
        // let var = Expression::Variable("x");

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);

        // FIXME: until we return a result type this will panic.
        // assert_eq!(w(&var, &mut env, &mut inst), Type::Unbound(1));
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(2)], Box::new(
                       Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number)

    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert!(unify(Type::Number, Type::Number, &mut env));
        assert_eq!(unify(Type::Number, Type::Boolean, &mut env), false);
        assert!(unify(Type::Unbound(1), Type::Number, &mut env));

        assert!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                      Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                      &mut env));
    }
}

fn main() {
    println!("patience.");
}
<|endoftext|>


<commit_before>use crate::utils::{
    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
    span_lint_and_sugg, walk_ptrs_ty,
};
use if_chain::if_chain;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use syntax::ast::{Name, UintTy};

declare_clippy_lint! {
    /// **What it does:** Checks for naive byte counts
    ///
    /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
    /// crate has methods to count your bytes faster, especially for large slices.
    ///
    /// **Known problems:** If you have predominantly small slices, the
    /// `bytecount::count(..)` method may actually be slower. However, if you can
    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
    /// faster in those cases.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// &my_data.filter(|&x| x == 0u8).count() // use bytecount::count instead
    /// ```
    pub NAIVE_BYTECOUNT,
    perf,
    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
}

#[derive(Copy, Clone)]
pub struct ByteCount;

impl LintPass for ByteCount {
    fn get_lints(&self) -> LintArray {
        lint_array!(NAIVE_BYTECOUNT)
    }

    fn name(&self) -> &'static str {
        "ByteCount"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount {
    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
        if_chain! {
            if let ExprKind::MethodCall(ref count, _, ref count_args) = expr.node;
            if count.ident.name == "count";
            if count_args.len() == 1;
            if let ExprKind::MethodCall(ref filter, _, ref filter_args) = count_args[0].node;
            if filter.ident.name == "filter";
            if filter_args.len() == 2;
            if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].node;
            then {
                let body = cx.tcx.hir().body(body_id);
                if_chain! {
                    if body.arguments.len() == 1;
                    if let Some(argname) = get_pat_name(&body.arguments[0].pat);
                    if let ExprKind::Binary(ref op, ref l, ref r) = body.value.node;
                    if op.node == BinOpKind::Eq;
                    if match_type(cx,
                               walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
                               &paths::SLICE_ITER);
                    then {
                        let needle = match get_path_name(l) {
                            Some(name) if check_arg(name, argname, r) => r,
                            _ => match get_path_name(r) {
                                Some(name) if check_arg(name, argname, l) => l,
                                _ => { return; }
                            }
                        };
                        if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).sty {
                            return;
                        }
                        let haystack = if let ExprKind::MethodCall(ref path, _, ref args) =
                                filter_args[0].node {
                            let p = path.ident.name;
                            if (p == "iter" || p == "iter_mut") && args.len() == 1 {
                                &args[0]
                            } else {
                                &filter_args[0]
                            }
                        } else {
                            &filter_args[0]
                        };
                        let mut applicability = Applicability::MachineApplicable;
                        span_lint_and_sugg(
                            cx,
                            NAIVE_BYTECOUNT,
                            expr.span,
                            "You appear to be counting bytes the naive way",
                            "Consider using the bytecount crate",
                            format!("bytecount::count({}, {})",
                                    snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
                                    snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
                            applicability,
                        );
                    }
                };
            }
        };
    }
}

fn check_arg(name: Name, arg: Name, needle: &Expr) -> bool {
    name == arg && !contains_name(name, needle)
}

fn get_path_name(expr: &Expr) -> Option<Name> {
    match expr.node {
        ExprKind::Box(ref e) | ExprKind::AddrOf(_, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => get_path_name(e),
        ExprKind::Block(ref b, _) => {
            if b.stmts.is_empty() {
                b.expr.as_ref().and_then(|p| get_path_name(p))
            } else {
                None
            }
        },
        ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
        _ => None,
    }
}
<commit_msg>Change naive_bytecount applicability MaybeIncorrect<commit_after>use crate::utils::{
    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
    span_lint_and_sugg, walk_ptrs_ty,
};
use if_chain::if_chain;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use syntax::ast::{Name, UintTy};

declare_clippy_lint! {
    /// **What it does:** Checks for naive byte counts
    ///
    /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
    /// crate has methods to count your bytes faster, especially for large slices.
    ///
    /// **Known problems:** If you have predominantly small slices, the
    /// `bytecount::count(..)` method may actually be slower. However, if you can
    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
    /// faster in those cases.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// &my_data.filter(|&x| x == 0u8).count() // use bytecount::count instead
    /// ```
    pub NAIVE_BYTECOUNT,
    perf,
    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
}

#[derive(Copy, Clone)]
pub struct ByteCount;

impl LintPass for ByteCount {
    fn get_lints(&self) -> LintArray {
        lint_array!(NAIVE_BYTECOUNT)
    }

    fn name(&self) -> &'static str {
        "ByteCount"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount {
    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
        if_chain! {
            if let ExprKind::MethodCall(ref count, _, ref count_args) = expr.node;
            if count.ident.name == "count";
            if count_args.len() == 1;
            if let ExprKind::MethodCall(ref filter, _, ref filter_args) = count_args[0].node;
            if filter.ident.name == "filter";
            if filter_args.len() == 2;
            if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].node;
            then {
                let body = cx.tcx.hir().body(body_id);
                if_chain! {
                    if body.arguments.len() == 1;
                    if let Some(argname) = get_pat_name(&body.arguments[0].pat);
                    if let ExprKind::Binary(ref op, ref l, ref r) = body.value.node;
                    if op.node == BinOpKind::Eq;
                    if match_type(cx,
                               walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
                               &paths::SLICE_ITER);
                    then {
                        let needle = match get_path_name(l) {
                            Some(name) if check_arg(name, argname, r) => r,
                            _ => match get_path_name(r) {
                                Some(name) if check_arg(name, argname, l) => l,
                                _ => { return; }
                            }
                        };
                        if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).sty {
                            return;
                        }
                        let haystack = if let ExprKind::MethodCall(ref path, _, ref args) =
                                filter_args[0].node {
                            let p = path.ident.name;
                            if (p == "iter" || p == "iter_mut") && args.len() == 1 {
                                &args[0]
                            } else {
                                &filter_args[0]
                            }
                        } else {
                            &filter_args[0]
                        };
                        let mut applicability = Applicability::MaybeIncorrect;
                        span_lint_and_sugg(
                            cx,
                            NAIVE_BYTECOUNT,
                            expr.span,
                            "You appear to be counting bytes the naive way",
                            "Consider using the bytecount crate",
                            format!("bytecount::count({}, {})",
                                    snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
                                    snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
                            applicability,
                        );
                    }
                };
            }
        };
    }
}

fn check_arg(name: Name, arg: Name, needle: &Expr) -> bool {
    name == arg && !contains_name(name, needle)
}

fn get_path_name(expr: &Expr) -> Option<Name> {
    match expr.node {
        ExprKind::Box(ref e) | ExprKind::AddrOf(_, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => get_path_name(e),
        ExprKind::Block(ref b, _) => {
            if b.stmts.is_empty() {
                b.expr.as_ref().and_then(|p| get_path_name(p))
            } else {
                None
            }
        },
        ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use crate::utils::paths;
use crate::utils::{
    in_macro, match_trait_method, match_type, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc::hir;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use syntax::ast::Ident;
use syntax::source_map::Span;

#[derive(Clone)]
pub struct Pass;

/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
/// `iterator.cloned()` instead
///
/// **Why is this bad?** Readability, this can be written more concisely
///
/// **Known problems:** Sometimes `.cloned()` requires stricter trait
/// bound than `.map(|e| e.clone())` (which works because of the coercion).
/// See [#498](https://github.com/rust-lang-nursery/rust-clippy/issues/498).
///
/// **Example:**
///
/// ```rust
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.map(|i| *i);
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.cloned();
/// ```
declare_clippy_lint! {
    pub MAP_CLONE,
    style,
    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(MAP_CLONE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
        if in_macro(e.span) {
            return;
        }

        if_chain! {
            if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node;
            if args.len() == 2;
            if method.ident.as_str() == "map";
            let ty = cx.tables.expr_ty(&args[0]);
            if match_type(cx, ty, &paths::OPTION) || match_trait_method(cx, e, &paths::ITERATOR);
            if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].node;
            let closure_body = cx.tcx.hir().body(body_id);
            let closure_expr = remove_blocks(&closure_body.value);
            then {
                match closure_body.arguments[0].pat.node {
                    hir::PatKind::Ref(ref inner, _) => if let hir::PatKind::Binding(
                        hir::BindingAnnotation::Unannotated, _, name, None
                    ) = inner.node {
                        if ident_eq(name, closure_expr) {
                            lint(cx, e.span, args[0].span);
                        }
                    },
                    hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
                        match closure_expr.node {
                            hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
                                if ident_eq(name, inner) && !cx.tables.expr_ty(inner).is_box() {
                                    lint(cx, e.span, args[0].span);
                                }
                            },
                            hir::ExprKind::MethodCall(ref method, _, ref obj) => {
                                if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone"
                                    && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {

                                    let obj_ty = cx.tables.expr_ty(&obj[0]);
                                    if let ty::Ref(..) = obj_ty.sty {
                                        lint(cx, e.span, args[0].span);
                                    } else {
                                        lint_needless_cloning(cx, e.span, args[0].span);
                                    }
                                }
                            },
                            _ => {},
                        }
                    },
                    _ => {},
                }
            }
        }
    }
}

fn ident_eq(name: Ident, path: &hir::Expr) -> bool {
    if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.node {
        path.segments.len() == 1 && path.segments[0].ident == name
    } else {
        false
    }
}

fn lint_needless_cloning(cx: &LateContext<'_, '_>, root: Span, receiver: Span) {
    span_lint_and_sugg(
        cx,
        MAP_CLONE,
        root.trim_start(receiver).unwrap(),
        "You are needlessly cloning iterator elements",
        "Remove the map call",
        String::new(),
        Applicability::MachineApplicable,
    )
}

fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span) {
    let mut applicability = Applicability::MachineApplicable;
    span_lint_and_sugg(
        cx,
        MAP_CLONE,
        replace,
        "You are using an explicit closure for cloning elements",
        "Consider calling the dedicated `cloned` method",
        format!(
            "{}.cloned()",
            snippet_with_applicability(cx, root, "..", &mut applicability)
        ),
        applicability,
    )
}
<commit_msg>Remove `map_clone` fixed known problem<commit_after>use crate::utils::paths;
use crate::utils::{
    in_macro, match_trait_method, match_type, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc::hir;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;
use syntax::ast::Ident;
use syntax::source_map::Span;

#[derive(Clone)]
pub struct Pass;

/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
/// `iterator.cloned()` instead
///
/// **Why is this bad?** Readability, this can be written more concisely
///
/// **Known problems:** None
///
/// **Example:**
///
/// ```rust
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.map(|i| *i);
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.cloned();
/// ```
declare_clippy_lint! {
    pub MAP_CLONE,
    style,
    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(MAP_CLONE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
        if in_macro(e.span) {
            return;
        }

        if_chain! {
            if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.node;
            if args.len() == 2;
            if method.ident.as_str() == "map";
            let ty = cx.tables.expr_ty(&args[0]);
            if match_type(cx, ty, &paths::OPTION) || match_trait_method(cx, e, &paths::ITERATOR);
            if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].node;
            let closure_body = cx.tcx.hir().body(body_id);
            let closure_expr = remove_blocks(&closure_body.value);
            then {
                match closure_body.arguments[0].pat.node {
                    hir::PatKind::Ref(ref inner, _) => if let hir::PatKind::Binding(
                        hir::BindingAnnotation::Unannotated, _, name, None
                    ) = inner.node {
                        if ident_eq(name, closure_expr) {
                            lint(cx, e.span, args[0].span);
                        }
                    },
                    hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
                        match closure_expr.node {
                            hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
                                if ident_eq(name, inner) && !cx.tables.expr_ty(inner).is_box() {
                                    lint(cx, e.span, args[0].span);
                                }
                            },
                            hir::ExprKind::MethodCall(ref method, _, ref obj) => {
                                if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone"
                                    && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {

                                    let obj_ty = cx.tables.expr_ty(&obj[0]);
                                    if let ty::Ref(..) = obj_ty.sty {
                                        lint(cx, e.span, args[0].span);
                                    } else {
                                        lint_needless_cloning(cx, e.span, args[0].span);
                                    }
                                }
                            },
                            _ => {},
                        }
                    },
                    _ => {},
                }
            }
        }
    }
}

fn ident_eq(name: Ident, path: &hir::Expr) -> bool {
    if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.node {
        path.segments.len() == 1 && path.segments[0].ident == name
    } else {
        false
    }
}

fn lint_needless_cloning(cx: &LateContext<'_, '_>, root: Span, receiver: Span) {
    span_lint_and_sugg(
        cx,
        MAP_CLONE,
        root.trim_start(receiver).unwrap(),
        "You are needlessly cloning iterator elements",
        "Remove the map call",
        String::new(),
        Applicability::MachineApplicable,
    )
}

fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span) {
    let mut applicability = Applicability::MachineApplicable;
    span_lint_and_sugg(
        cx,
        MAP_CLONE,
        replace,
        "You are using an explicit closure for cloning elements",
        "Consider calling the dedicated `cloned` method",
        format!(
            "{}.cloned()",
            snippet_with_applicability(cx, root, "..", &mut applicability)
        ),
        applicability,
    )
}
<|endoftext|>


<commit_before>//! API to generate `.rs` files.
//!
//! This API requires `protoc` command present in `$PATH`.
//!
//! ```
//! extern crate protoc_rust;
//!
//! fn main() {
//!     protoc_rust::Codegen::new()
//!         .out_dir("src/protos")
//!         .inputs(&["protos/a.proto", "protos/b.proto"]),
//!         .include("protos")
//!         .run()
//!         .expect("Running protoc failed.");
//! }
//! ```
//!
//! It is advisable that `protoc-rust` build-dependecy version be the same as
//! `protobuf` dependency.
//!
//! The alternative is to use `protobuf-codegen-pure`.

#![deny(missing_docs)]
#![deny(intra_doc_link_resolution_failure)]

extern crate tempfile;

extern crate protobuf;
extern crate protobuf_codegen;
extern crate protoc;

use std::fs;
use std::io;
use std::path::{Path, PathBuf};

pub use protoc::Error;
pub use protoc::Result;

pub use protobuf_codegen::Customize;

/// `Protoc --rust_out...` args
#[derive(Debug, Default)]
pub struct Codegen {
    /// --lang_out= param
    out_dir: PathBuf,
    /// -I args
    includes: Vec<PathBuf>,
    /// List of .proto files to compile
    inputs: Vec<PathBuf>,
    /// Customize code generation
    customize: Customize,
}

impl Codegen {
    /// Arguments to the `protoc` found in `$PATH`
    pub fn new() -> Self {
        Self::default()
    }

    /// Set `--LANG_out=...` param
    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
        self.out_dir = out_dir.as_ref().to_owned();
        self
    }

    /// Append a path to `-I` args
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Append multiple paths to `-I` args
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Append a `.proto` file path to compile
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Append multiple `.proto` file paths to compile
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Set options to customize code generation
    pub fn customize(&mut self, customize: Customize) -> &mut Self {
        self.customize = customize;
        self
    }

    /// Like `protoc --rust_out=...` but without requiring `protoc-gen-rust` command in `$PATH`.
    pub fn run(&self) -> Result<()> {
        let protoc = protoc::Protoc::from_env_path();
        protoc.check()?;

        let temp_dir = tempfile::Builder::new().prefix("protoc-rust").tempdir()?;
        let temp_file = temp_dir.path().join("descriptor.pbbin");

        protoc
            .descriptor_set_out_args()
            .out(&temp_file)
            .includes(&self.includes)
            .inputs(&self.inputs)
            .include_imports(true)
            .write_descriptor_set()?;

        let fds = fs::read(temp_file)?;
        drop(temp_dir);

        let fds: protobuf::descriptor::FileDescriptorSet = protobuf::parse_from_bytes(&fds)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

        let default_includes = vec![PathBuf::from(".")];
        let includes = if self.includes.is_empty() {
            &default_includes
        } else {
            &self.includes
        };

        let mut files_to_generate = Vec::new();
        'outer: for file in &self.inputs {
            for include in includes {
                if let Some(truncated) = remove_path_prefix(file, include) {
                    files_to_generate.push(truncated.to_owned());
                    continue 'outer;
                }
            }

            return Err(Error::new(
                io::ErrorKind::Other,
                format!("file {:?} is not found in includes {:?}", file, includes),
            ));
        }

        protobuf_codegen::gen_and_write(
            &fds.file,
            &format!("protoc {}", protoc.version()?),
            &files_to_generate,
            &self.out_dir,
            &self.customize,
        )
    }
}

fn remove_path_prefix<'a>(mut path: &'a Path, mut prefix: &Path) -> Option<&'a Path> {
    path = path.strip_prefix(".").unwrap_or(path);
    prefix = prefix.strip_prefix(".").unwrap_or(prefix);
    path.strip_prefix(prefix).ok()
}

#[test]
fn test_remove_path_prefix() {
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("../xxx/abc.proto"), Path::new("../xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("."))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("./"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy/"))
    );
}
<commit_msg>Option to specify protoc command path for Codegen<commit_after>//! API to generate `.rs` files.
//!
//! This API requires `protoc` command present in `$PATH`
//! or [passed explicitly to `Codegen` object](crate::Codegen::protoc_path).
//!
//! ```no_run
//! extern crate protoc_rust;
//!
//! fn main() {
//!     protoc_rust::Codegen::new()
//!         .out_dir("src/protos")
//!         .inputs(&["protos/a.proto", "protos/b.proto"])
//!         .include("protos")
//!         .run()
//!         .expect("Running protoc failed.");
//! }
//! ```
//!
//! It is advisable that `protoc-rust` build-dependecy version be the same as
//! `protobuf` dependency.
//!
//! The alternative is to use `protobuf-codegen-pure`.

#![deny(missing_docs)]
#![deny(intra_doc_link_resolution_failure)]

extern crate tempfile;

extern crate protobuf;
extern crate protobuf_codegen;
extern crate protoc;

use std::fs;
use std::io;
use std::path::{Path, PathBuf};

pub use protoc::Error;
pub use protoc::Result;

pub use protobuf_codegen::Customize;
use protoc::Protoc;

/// `Protoc --rust_out...` args
#[derive(Debug, Default)]
pub struct Codegen {
    /// --lang_out= param
    out_dir: PathBuf,
    /// -I args
    includes: Vec<PathBuf>,
    /// List of .proto files to compile
    inputs: Vec<PathBuf>,
    /// Customize code generation
    customize: Customize,
    /// Protoc command path
    protoc: Option<Protoc>,
}

impl Codegen {
    /// Arguments to the `protoc` found in `$PATH`
    pub fn new() -> Self {
        Self::default()
    }

    /// Set `--LANG_out=...` param
    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
        self.out_dir = out_dir.as_ref().to_owned();
        self
    }

    /// Append a path to `-I` args
    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
        self.includes.push(include.as_ref().to_owned());
        self
    }

    /// Append multiple paths to `-I` args
    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for include in includes {
            self.include(include);
        }
        self
    }

    /// Append a `.proto` file path to compile
    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
        self.inputs.push(input.as_ref().to_owned());
        self
    }

    /// Append multiple `.proto` file paths to compile
    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
        for input in inputs {
            self.input(input);
        }
        self
    }

    /// Specify `protoc` command path to be used when invoking code generation.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # mod protoc_bin_vendored {
    /// #   pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
    /// #       unimplemented!()
    /// #   }
    /// # }
    ///
    /// use protoc_rust::Codegen;
    ///
    /// Codegen::new()
    ///     .protoc_path(protoc_bin_vendored::protoc_bin_path().unwrap())
    ///     // ...
    ///     .run()
    ///     .unwrap();
    /// ```
    pub fn protoc_path(&mut self, protoc: impl Into<PathBuf>) -> &mut Self {
        self.protoc = Some(Protoc::from_path(&protoc.into()));
        self
    }

    /// Set options to customize code generation
    pub fn customize(&mut self, customize: Customize) -> &mut Self {
        self.customize = customize;
        self
    }

    /// Like `protoc --rust_out=...` but without requiring `protoc-gen-rust` command in `$PATH`.
    pub fn run(&self) -> Result<()> {
        let protoc = match self.protoc.clone() {
            Some(protoc) => protoc,
            None => Protoc::from_env_path(),
        };
        protoc.check()?;

        let temp_dir = tempfile::Builder::new().prefix("protoc-rust").tempdir()?;
        let temp_file = temp_dir.path().join("descriptor.pbbin");

        protoc
            .descriptor_set_out_args()
            .out(&temp_file)
            .includes(&self.includes)
            .inputs(&self.inputs)
            .include_imports(true)
            .write_descriptor_set()?;

        let fds = fs::read(temp_file)?;
        drop(temp_dir);

        let fds: protobuf::descriptor::FileDescriptorSet = protobuf::parse_from_bytes(&fds)
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

        let default_includes = vec![PathBuf::from(".")];
        let includes = if self.includes.is_empty() {
            &default_includes
        } else {
            &self.includes
        };

        let mut files_to_generate = Vec::new();
        'outer: for file in &self.inputs {
            for include in includes {
                if let Some(truncated) = remove_path_prefix(file, include) {
                    files_to_generate.push(truncated.to_owned());
                    continue 'outer;
                }
            }

            return Err(Error::new(
                io::ErrorKind::Other,
                format!("file {:?} is not found in includes {:?}", file, includes),
            ));
        }

        protobuf_codegen::gen_and_write(
            &fds.file,
            &format!("protoc {}", protoc.version()?),
            &files_to_generate,
            &self.out_dir,
            &self.customize,
        )
    }
}

fn remove_path_prefix<'a>(mut path: &'a Path, mut prefix: &Path) -> Option<&'a Path> {
    path = path.strip_prefix(".").unwrap_or(path);
    prefix = prefix.strip_prefix(".").unwrap_or(prefix);
    path.strip_prefix(prefix).ok()
}

#[test]
fn test_remove_path_prefix() {
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("../xxx/abc.proto"), Path::new("../xxx/"))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("."))
    );
    assert_eq!(
        Some(Path::new("abc.proto")),
        remove_path_prefix(Path::new("abc.proto"), Path::new("./"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy"))
    );
    assert_eq!(
        None,
        remove_path_prefix(Path::new("xxx/abc.proto"), Path::new("yyy/"))
    );
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate timely;
extern crate differential_dataflow;

use timely::dataflow::operators::*;

use rand::{Rng, SeedableRng, StdRng};

use differential_dataflow::input::Input;
use differential_dataflow::AsCollection;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::group::Group;
use differential_dataflow::operators::join::JoinCore;
use differential_dataflow::operators::Iterate;
use differential_dataflow::operators::Consolidate;

// use differential_dataflow::trace::implementations::ord::OrdValSpine;
// use differential_dataflow::trace::{Cursor, Trace};
// use differential_dataflow::trace::Batch;
// use differential_dataflow::hashable::OrdWrapper;
// use differential_dataflow::trace::TraceReader;

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();
    let pre: usize = std::env::args().nth(4).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().position(|x| x == "inspect").is_some();

    // define a new timely dataflow computation.
    timely::execute_from_args(std::env::args().skip(4), move |worker| {

        let timer = ::std::time::Instant::now();

        let index = worker.index();
        let peers = worker.peers();

        let mut probe = timely::dataflow::operators::probe::Handle::new();

        // create a dataflow managing an ever-changing edge collection.
    	let mut graph = worker.dataflow(|scope| {

            // create a source operator which will produce random edges and delete them.
            timely::dataflow::operators::generic::source(scope, "RandomGraph", |mut capability| {

                let seed: &[_] = &[1, 2, 3, index];
                let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
                let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions

                let mut additions = 0;
                let mut deletions = 0;

                let handle = probe.clone();

                move |output| {

                    // do nothing if the probe is not caught up to us
                    if !handle.less_than(capability.time()) {

                        let mut time = capability.time().clone();
                        // println!("{:?}\tintroducing edges for batch starting {:?}", timer.elapsed(), time);

                        {   // scope to allow session to drop, un-borrow.
                            let mut session = output.session(&capability);

                            // we want to send at times.inner + (0 .. batch).
                            for _ in 0 .. batch {

                                while additions < time.inner + edges {
                                    if additions % peers == index {
                                        let src = rng1.gen_range(0, nodes);
                                        let dst = rng1.gen_range(0, nodes);
                                        session.give(((src, dst), time, 1));
                                    }
                                    additions += 1;
                                }
                                while deletions < time.inner {
                                    if deletions % peers == index {
                                        let src = rng2.gen_range(0, nodes);
                                        let dst = rng2.gen_range(0, nodes);
                                        session.give(((src, dst), time, -1));
                                    }
                                    deletions += 1;
                                }

                                time.inner += 1;
                            }
                        }

                        // println!("downgrading {:?} to {:?}", capability, time);
                        capability.downgrade(&time);
                    }
                }
            })
            .probe_with(&mut probe)
            .as_collection()
            .arrange_by_key()
            .trace
        });

        println!("{:?}:\tloading edges", timer.elapsed());

        for _ in 0 .. pre {
            worker.step();
        }

        println!("{:?}\tedges loaded; building query dataflows", timer.elapsed());

        let mut roots = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, roots) = scope.new_collection();
            let roots = roots.map(|x| (x, 0));

            // repeatedly update minimal distances each node can be reached from each root
            roots.iterate(|dists| {

                let edges = edges.enter(&dists.scope());
                let roots = roots.enter(&dists.scope());

                dists.arrange_by_key()
                     .join_core(&edges, |_k,l,d| Some((*d, l+1)))
                     .concat(&roots)
                     .group(|_, s, t| t.push((*s[0].0, 1)))
            })
            .map(|(_node, dist)| dist)
            .consolidate()
            .inspect(|x| println!("distance update: {:?}", x))
            .probe_with(&mut probe);

            input
        });

        let mut query = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, query) = scope.new_collection();

            query.map(|x| (x, x))
                 .arrange_by_key()
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .arrange_by_key()
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .arrange_by_key()
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .filter(move |_| inspect)
                 .map(|x| x.1)
                 .consolidate()
                 .inspect(|x| println!("{:?}", x))
                 .probe_with(&mut probe);

            input
        });

        println!("{:?}\tquery dataflows built; querying", timer.elapsed());

        // the trace will not compact unless we release capabilities.
        // we drop rather than continually downgrade them as we run.
        drop(graph);

        if batch > 0 {
            for round in 0 .. {

                let mut time = query.time().clone();

                // roots.insert(round % nodes);
                if index == 0 {
                    query.insert(round % nodes);
                }

                time.inner += batch;

                roots.advance_to(time.inner); roots.flush();
                query.advance_to(time.inner); query.flush();

                if index == 0 {
                    query.remove(round % nodes);
                }

                worker.step_while(|| probe.less_than(&time));

                println!("{:?}\tquery round {:?} complete", timer.elapsed(), round);
            }
        }
    }).unwrap();
}<commit_msg>arrange uses abom batch<commit_after>extern crate rand;
extern crate timely;
extern crate differential_dataflow;

use timely::dataflow::operators::*;

use rand::{Rng, SeedableRng, StdRng};

use differential_dataflow::input::Input;
use differential_dataflow::AsCollection;
use differential_dataflow::operators::arrange::{Arrange, ArrangeByKey};
use differential_dataflow::operators::group::Group;
use differential_dataflow::operators::join::JoinCore;
use differential_dataflow::operators::Iterate;
use differential_dataflow::operators::Consolidate;

use differential_dataflow::trace::Trace;
use differential_dataflow::trace::implementations::ord::OrdValSpineAbom;

// use differential_dataflow::trace::implementations::ord::OrdValSpine;
// use differential_dataflow::trace::{Cursor, Trace};
// use differential_dataflow::trace::Batch;
// use differential_dataflow::hashable::OrdWrapper;
// use differential_dataflow::trace::TraceReader;

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();
    let pre: usize = std::env::args().nth(4).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().position(|x| x == "inspect").is_some();

    // define a new timely dataflow computation.
    timely::execute_from_args(std::env::args().skip(4), move |worker| {

        let timer = ::std::time::Instant::now();

        let index = worker.index();
        let peers = worker.peers();

        let mut probe = timely::dataflow::operators::probe::Handle::new();

        // create a dataflow managing an ever-changing edge collection.
    	let mut graph = worker.dataflow(|scope| {

            // create a source operator which will produce random edges and delete them.
            timely::dataflow::operators::generic::source(scope, "RandomGraph", |mut capability| {

                let seed: &[_] = &[1, 2, 3, index];
                let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
                let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions

                let mut additions = 0;
                let mut deletions = 0;

                let handle = probe.clone();

                move |output| {

                    // do nothing if the probe is not caught up to us
                    if !handle.less_than(capability.time()) {

                        let mut time = capability.time().clone();
                        // println!("{:?}\tintroducing edges for batch starting {:?}", timer.elapsed(), time);

                        {   // scope to allow session to drop, un-borrow.
                            let mut session = output.session(&capability);

                            // we want to send at times.inner + (0 .. batch).
                            for _ in 0 .. batch {

                                while additions < time.inner + edges {
                                    if additions % peers == index {
                                        let src = rng1.gen_range(0, nodes);
                                        let dst = rng1.gen_range(0, nodes);
                                        session.give(((src, dst), time, 1));
                                    }
                                    additions += 1;
                                }
                                while deletions < time.inner {
                                    if deletions % peers == index {
                                        let src = rng2.gen_range(0, nodes);
                                        let dst = rng2.gen_range(0, nodes);
                                        session.give(((src, dst), time, -1));
                                    }
                                    deletions += 1;
                                }

                                time.inner += 1;
                            }
                        }

                        // println!("downgrading {:?} to {:?}", capability, time);
                        capability.downgrade(&time);
                    }
                }
            })
            .probe_with(&mut probe)
            .as_collection()
            // .arrange_by_key()
            .arrange(OrdValSpineAbom::new())
            .trace
        });

        println!("{:?}:\tloading edges", timer.elapsed());

        for _ in 0 .. pre {
            worker.step();
        }

        println!("{:?}\tedges loaded; building query dataflows", timer.elapsed());

        let mut roots = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, roots) = scope.new_collection();
            let roots = roots.map(|x| (x, 0));

            // repeatedly update minimal distances each node can be reached from each root
            roots.iterate(|dists| {

                let edges = edges.enter(&dists.scope());
                let roots = roots.enter(&dists.scope());

                dists.arrange_by_key()
                     .join_core(&edges, |_k,l,d| Some((*d, l+1)))
                     .concat(&roots)
                     .group(|_, s, t| t.push((*s[0].0, 1)))
            })
            .map(|(_node, dist)| dist)
            .consolidate()
            .inspect(|x| println!("distance update: {:?}", x))
            .probe_with(&mut probe);

            input
        });

        let mut query = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, query) = scope.new_collection();

            query.map(|x| (x, x))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .filter(move |_| inspect)
                 .map(|x| x.1)
                 .consolidate()
                 .inspect(|x| println!("{:?}", x))
                 .probe_with(&mut probe);

            input
        });

        println!("{:?}\tquery dataflows built; querying", timer.elapsed());

        // the trace will not compact unless we release capabilities.
        // we drop rather than continually downgrade them as we run.
        drop(graph);

        if batch > 0 {
            for round in 0 .. {

                let mut time = query.time().clone();

                // roots.insert(round % nodes);
                if index == 0 {
                    query.insert(round % nodes);
                }

                time.inner += batch;

                roots.advance_to(time.inner); roots.flush();
                query.advance_to(time.inner); query.flush();

                if index == 0 {
                    query.remove(round % nodes);
                }

                worker.step_while(|| probe.less_than(&time));

                println!("{:?}\tquery round {:?} complete", timer.elapsed(), round);
            }
        }
    }).unwrap();
}<|endoftext|>


<commit_before>use std::io;
use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
use std::time::Duration;

/// A transport for TCP.
pub struct TcpTransport(pub A);

impl super::Transport for TcpTransport {
    type Listener = TcpListener;
    fn bind(&self) -> io::Result<TcpListener> {
        TcpListener::bind(&self.0)
    }
}

impl super::Transport for A {
    type Listener = TcpListener;
    fn bind(&self) -> io::Result<TcpListener> {
        TcpListener::bind(self)
    }
}

impl super::Listener for TcpListener {
    type Dialer = TcpDialer<SocketAddr>;
    type Stream = TcpStream;
    fn accept(&self) -> io::Result<TcpStream> {
        self.accept().map(|(stream, _)| stream)
    }
    fn dialer(&self) -> io::Result<TcpDialer<SocketAddr>> {
        self.local_addr().map(|addr| TcpDialer(addr))
    }
}

impl super::Stream for TcpStream {
    fn try_clone(&self) -> io::Result<Self> {
        self.try_clone()
    }
    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.set_read_timeout(dur)
    }
    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.set_write_timeout(dur)
    }
    fn shutdown(&self) -> io::Result<()> {
        self.shutdown(::std::net::Shutdown::Both)
    }
}

/// Connects to a socket address.
pub struct TcpDialer(pub A) where A: ToSocketAddrs;
impl super::Dialer for TcpDialer
    where A: ToSocketAddrs
{
    type Stream = TcpStream;
    fn dial(&self) -> io::Result<TcpStream> {
        TcpStream::connect(&self.0)
    }
}
impl super::Dialer for str {
    type Stream = TcpStream;
    fn dial(&self) -> io::Result<TcpStream> {
        TcpStream::connect(self)
    }
}
<commit_msg>Add a line between impl and struct<commit_after>use std::io;
use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
use std::time::Duration;

/// A transport for TCP.
pub struct TcpTransport(pub A);

impl super::Transport for TcpTransport {
    type Listener = TcpListener;
    fn bind(&self) -> io::Result<TcpListener> {
        TcpListener::bind(&self.0)
    }
}

impl super::Transport for A {
    type Listener = TcpListener;
    fn bind(&self) -> io::Result<TcpListener> {
        TcpListener::bind(self)
    }
}

impl super::Listener for TcpListener {
    type Dialer = TcpDialer<SocketAddr>;
    type Stream = TcpStream;
    fn accept(&self) -> io::Result<TcpStream> {
        self.accept().map(|(stream, _)| stream)
    }
    fn dialer(&self) -> io::Result<TcpDialer<SocketAddr>> {
        self.local_addr().map(|addr| TcpDialer(addr))
    }
}

impl super::Stream for TcpStream {
    fn try_clone(&self) -> io::Result<Self> {
        self.try_clone()
    }
    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.set_read_timeout(dur)
    }
    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.set_write_timeout(dur)
    }
    fn shutdown(&self) -> io::Result<()> {
        self.shutdown(::std::net::Shutdown::Both)
    }
}

/// Connects to a socket address.
pub struct TcpDialer(pub A) where A: ToSocketAddrs;

impl super::Dialer for TcpDialer
    where A: ToSocketAddrs
{
    type Stream = TcpStream;
    fn dial(&self) -> io::Result<TcpStream> {
        TcpStream::connect(&self.0)
    }
}
impl super::Dialer for str {
    type Stream = TcpStream;
    fn dial(&self) -> io::Result<TcpStream> {
        TcpStream::connect(self)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Apply content encodings (such as gzip compression) to the response.
//!
//! This module provides access to the content encodings supported by a request as well as
//! a function to automatically apply common content encodings to a response.
//! # Basic example
//!
//! Here is a basic example showing how to use content encodings:
//!
//! ```
//! use rouille::Request;
//! use rouille::Response;
//! use rouille::content_encoding;
//!
//! fn handle_request(request: &Request) -> Response {
//!     let mut text = String::new();
//!     for encoding in content_encoding::accepted_content_encodings(request) {
//!         text.push_str(encoding);
//!         text.push('\n');
//!     }
//!     let response = Response::text(&text);
//!     content_encoding::apply(&request, response)
//! }
//! ```
use std::str;
use Request;
use Response;

/// Applies content encoding to the response.
///
/// Analyzes the `Accept-Encoding` header of the request. If one of the encodings is recognized and
/// supported by rouille, it adds a `Content-Encoding` header to the `Response` and encodes its
/// body.
///
/// If the response already has a `Content-Encoding` header, this function is a no-op.
/// If the response has a `Content-Type` header that isn't textual content, this function is a
/// no-op.
///
/// The gzip encoding is supported only if you enable the `gzip` feature of rouille (which is
/// enabled by default).
///
/// # Example
///
/// ```rust
/// use rouille::content_encoding;
/// use rouille::Request;
/// use rouille::Response;
///
/// fn handle(request: &Request) -> Response {
///     content_encoding::apply(request, Response::text("hello world"))
/// }
/// ```
pub fn apply(request: &Request, response: Response) -> Response {
    // Only text should be encoded. Otherwise just return.
    if !response_is_text(&response) {
        return response;
    }

    // If any of the response's headers is equal to `Content-Encoding`, ignore the function
    // call and return immediately.
    if response
        .headers
        .iter()
        .any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
    {
        return response;
    }

    // Put the response in an Option for later.
    let mut response = Some(response);

    // Now let's get the list of content encodings accepted by the request.
    // The list should be ordered from the most desired to the list desired.
    // TODO: use input::priority_header_preferred instead
    for encoding in accepted_content_encodings(request) {
        // Try the brotli encoding.
        if brotli(encoding, &mut response) {
            return response.take().unwrap();
        }

        // Try the gzip encoding.
        if gzip(encoding, &mut response) {
            return response.take().unwrap();
        }

        // The identity encoding is always supported.
        if encoding.eq_ignore_ascii_case("identity") {
            return response.take().unwrap();
        }
    }

    // No encoding accepted, don't do anything.
    response.take().unwrap()
}

// Returns true if the Content-Type of the response is a type that should be encoded.
// Since encoding is purely an optimization, it's not a problem if the function sometimes has
// false positives or false negatives.
fn response_is_text(response: &Response) -> bool {
    response.headers.iter().any(|&(ref key, ref value)| {
        if !key.eq_ignore_ascii_case("Content-Type") {
            return false;
        }

        // TODO: perform case-insensitive comparison
        value.starts_with("text/")
            || value.contains("javascript")
            || value.contains("json")
            || value.contains("xml")
            || value.contains("font")
    })
}

/// Returns an iterator of the list of content encodings accepted by the request.
///
/// # Example
///
/// ```
/// use rouille::{Request, Response};
/// use rouille::content_encoding;
///
/// fn handle(request: &Request) -> Response {
///     for encoding in content_encoding::accepted_content_encodings(request) {
///         // ...
///     }
///
///     // ...
/// # panic!()
/// }
/// ```
pub fn accepted_content_encodings(request: &Request) -> AcceptedContentEncodingsIter {
    let elems = request.header("Accept-Encoding").unwrap_or("").split(',');
    AcceptedContentEncodingsIter { elements: elems }
}

/// Iterator to the list of content encodings accepted by a request.
pub struct AcceptedContentEncodingsIter<'a> {
    elements: str::Split<'a, char>,
}

impl<'a> Iterator for AcceptedContentEncodingsIter<'a> {
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        loop {
            match self.elements.next() {
                None => return None,
                Some(e) => {
                    let e = e.trim();
                    if !e.is_empty() {
                        return Some(e);
                    }
                }
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, max) = self.elements.size_hint();
        (0, max)
    }
}

#[cfg(feature = "gzip")]
fn gzip(e: &str, response: &mut Option<Response>) -> bool {
    use deflate::deflate_bytes_gzip;
    use std::io;
    use std::mem;
    use ResponseBody;

    if !e.eq_ignore_ascii_case("gzip") {
        return false;
    }

    let response = response.as_mut().unwrap();
    response
        .headers
        .push(("Content-Encoding".into(), "gzip".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (mut raw_data, size) = previous_body.into_reader_and_size();
    let mut src = match size {
        Some(size) => Vec::with_capacity(size),
        None => Vec::new(),
    };
    io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
    let zipped = deflate_bytes_gzip(&src);
    response.data = ResponseBody::from_data(zipped);
    true
}

#[cfg(not(feature = "gzip"))]
#[inline]
fn gzip(e: &str, response: &mut Option<Response>) -> bool {
    false
}

#[cfg(feature = "brotli")]
fn brotli(e: &str, response: &mut Option<Response>) -> bool {
    use brotli2::read::BrotliEncoder;
    use std::mem;
    use ResponseBody;

    if !e.eq_ignore_ascii_case("br") {
        return false;
    }

    let response = response.as_mut().unwrap();
    response
        .headers
        .push(("Content-Encoding".into(), "br".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (raw_data, _) = previous_body.into_reader_and_size();
    response.data = ResponseBody::from_reader(BrotliEncoder::new(raw_data, 6));
    true
}

#[cfg(not(feature = "brotli"))]
#[inline]
fn brotli(e: &str, response: &mut Option<Response>) -> bool {
    false
}

#[cfg(test)]
mod tests {
    use content_encoding;
    use Request;

    #[test]
    fn no_req_encodings() {
        let request = Request::fake_http("GET", "/", vec![], vec![]);
        assert_eq!(
            content_encoding::accepted_content_encodings(&request).count(),
            0
        );
    }

    #[test]
    fn empty_req_encodings() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };

        assert_eq!(
            content_encoding::accepted_content_encodings(&request).count(),
            0
        );
    }

    #[test]
    fn one_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };

        let mut list = content_encoding::accepted_content_encodings(&request);
        assert_eq!(list.next().unwrap(), "foo");
        assert_eq!(list.next(), None);
    }

    #[test]
    fn multi_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "foo, bar".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };

        let mut list = content_encoding::accepted_content_encodings(&request);
        assert_eq!(list.next().unwrap(), "foo");
        assert_eq!(list.next().unwrap(), "bar");
        assert_eq!(list.next(), None);
    }

    // TODO: more tests for encoding stuff
}
<commit_msg>Fix TODO: Case-insensitive comparison in response_is_text()<commit_after>// Copyright (c) 2016 The Rouille developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Apply content encodings (such as gzip compression) to the response.
//!
//! This module provides access to the content encodings supported by a request as well as
//! a function to automatically apply common content encodings to a response.
//! # Basic example
//!
//! Here is a basic example showing how to use content encodings:
//!
//! ```
//! use rouille::Request;
//! use rouille::Response;
//! use rouille::content_encoding;
//!
//! fn handle_request(request: &Request) -> Response {
//!     let mut text = String::new();
//!     for encoding in content_encoding::accepted_content_encodings(request) {
//!         text.push_str(encoding);
//!         text.push('\n');
//!     }
//!     let response = Response::text(&text);
//!     content_encoding::apply(&request, response)
//! }
//! ```
use std::str;
use Request;
use Response;

/// Applies content encoding to the response.
///
/// Analyzes the `Accept-Encoding` header of the request. If one of the encodings is recognized and
/// supported by rouille, it adds a `Content-Encoding` header to the `Response` and encodes its
/// body.
///
/// If the response already has a `Content-Encoding` header, this function is a no-op.
/// If the response has a `Content-Type` header that isn't textual content, this function is a
/// no-op.
///
/// The gzip encoding is supported only if you enable the `gzip` feature of rouille (which is
/// enabled by default).
///
/// # Example
///
/// ```rust
/// use rouille::content_encoding;
/// use rouille::Request;
/// use rouille::Response;
///
/// fn handle(request: &Request) -> Response {
///     content_encoding::apply(request, Response::text("hello world"))
/// }
/// ```
pub fn apply(request: &Request, response: Response) -> Response {
    // Only text should be encoded. Otherwise just return.
    if !response_is_text(&response) {
        return response;
    }

    // If any of the response's headers is equal to `Content-Encoding`, ignore the function
    // call and return immediately.
    if response
        .headers
        .iter()
        .any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
    {
        return response;
    }

    // Put the response in an Option for later.
    let mut response = Some(response);

    // Now let's get the list of content encodings accepted by the request.
    // The list should be ordered from the most desired to the list desired.
    // TODO: use input::priority_header_preferred instead
    for encoding in accepted_content_encodings(request) {
        // Try the brotli encoding.
        if brotli(encoding, &mut response) {
            return response.take().unwrap();
        }

        // Try the gzip encoding.
        if gzip(encoding, &mut response) {
            return response.take().unwrap();
        }

        // The identity encoding is always supported.
        if encoding.eq_ignore_ascii_case("identity") {
            return response.take().unwrap();
        }
    }

    // No encoding accepted, don't do anything.
    response.take().unwrap()
}

// Returns true if the Content-Type of the response is a type that should be encoded.
// Since encoding is purely an optimization, it's not a problem if the function sometimes has
// false positives or false negatives.
fn response_is_text(response: &Response) -> bool {
    response.headers.iter().any(|&(ref key, ref value)| {
        if !key.eq_ignore_ascii_case("Content-Type") {
            return false;
        }

        let content_type = value.to_lowercase();
        content_type.starts_with("text/")
            || content_type.contains("javascript")
            || content_type.contains("json")
            || content_type.contains("xml")
            || content_type.contains("font")
    })
}

/// Returns an iterator of the list of content encodings accepted by the request.
///
/// # Example
///
/// ```
/// use rouille::{Request, Response};
/// use rouille::content_encoding;
///
/// fn handle(request: &Request) -> Response {
///     for encoding in content_encoding::accepted_content_encodings(request) {
///         // ...
///     }
///
///     // ...
/// # panic!()
/// }
/// ```
pub fn accepted_content_encodings(request: &Request) -> AcceptedContentEncodingsIter {
    let elems = request.header("Accept-Encoding").unwrap_or("").split(',');
    AcceptedContentEncodingsIter { elements: elems }
}

/// Iterator to the list of content encodings accepted by a request.
pub struct AcceptedContentEncodingsIter<'a> {
    elements: str::Split<'a, char>,
}

impl<'a> Iterator for AcceptedContentEncodingsIter<'a> {
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<&'a str> {
        loop {
            match self.elements.next() {
                None => return None,
                Some(e) => {
                    let e = e.trim();
                    if !e.is_empty() {
                        return Some(e);
                    }
                }
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, max) = self.elements.size_hint();
        (0, max)
    }
}

#[cfg(feature = "gzip")]
fn gzip(e: &str, response: &mut Option<Response>) -> bool {
    use deflate::deflate_bytes_gzip;
    use std::io;
    use std::mem;
    use ResponseBody;

    if !e.eq_ignore_ascii_case("gzip") {
        return false;
    }

    let response = response.as_mut().unwrap();
    response
        .headers
        .push(("Content-Encoding".into(), "gzip".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (mut raw_data, size) = previous_body.into_reader_and_size();
    let mut src = match size {
        Some(size) => Vec::with_capacity(size),
        None => Vec::new(),
    };
    io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
    let zipped = deflate_bytes_gzip(&src);
    response.data = ResponseBody::from_data(zipped);
    true
}

#[cfg(not(feature = "gzip"))]
#[inline]
fn gzip(e: &str, response: &mut Option<Response>) -> bool {
    false
}

#[cfg(feature = "brotli")]
fn brotli(e: &str, response: &mut Option<Response>) -> bool {
    use brotli2::read::BrotliEncoder;
    use std::mem;
    use ResponseBody;

    if !e.eq_ignore_ascii_case("br") {
        return false;
    }

    let response = response.as_mut().unwrap();
    response
        .headers
        .push(("Content-Encoding".into(), "br".into()));
    let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
    let (raw_data, _) = previous_body.into_reader_and_size();
    response.data = ResponseBody::from_reader(BrotliEncoder::new(raw_data, 6));
    true
}

#[cfg(not(feature = "brotli"))]
#[inline]
fn brotli(e: &str, response: &mut Option<Response>) -> bool {
    false
}

#[cfg(test)]
mod tests {
    use content_encoding;
    use Request;

    #[test]
    fn no_req_encodings() {
        let request = Request::fake_http("GET", "/", vec![], vec![]);
        assert_eq!(
            content_encoding::accepted_content_encodings(&request).count(),
            0
        );
    }

    #[test]
    fn empty_req_encodings() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };

        assert_eq!(
            content_encoding::accepted_content_encodings(&request).count(),
            0
        );
    }

    #[test]
    fn one_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };

        let mut list = content_encoding::accepted_content_encodings(&request);
        assert_eq!(list.next().unwrap(), "foo");
        assert_eq!(list.next(), None);
    }

    #[test]
    fn multi_req_encoding() {
        let request = {
            let h = vec![("Accept-Encoding".to_owned(), "foo, bar".to_owned())];
            Request::fake_http("GET", "/", h, vec![])
        };

        let mut list = content_encoding::accepted_content_encodings(&request);
        assert_eq!(list.next().unwrap(), "foo");
        assert_eq!(list.next().unwrap(), "bar");
        assert_eq!(list.next(), None);
    }

    // TODO: more tests for encoding stuff
}
<|endoftext|>