<commit_before>use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};

use walkdir::WalkDir;

// The maximum length allowed for stderr files.
//
// We limit this because small files are easier to deal with than bigger files.
const LENGTH_LIMIT: usize = 200;

pub fn check() {
    let exceeding_files: Vec<_> = exceeding_stderr_files();

    if !exceeding_files.is_empty() {
        eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT);
        for path in exceeding_files {
            println!("{}", path.display());
        }
        std::process::exit(1);
    }
}

fn exceeding_stderr_files() -> Vec<PathBuf> {
    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
    WalkDir::new("../tests/ui")
        .into_iter()
        .filter_map(Result::ok)
        .filter_map(|e| {
            let p = e.into_path();
            if p.extension() == Some(OsStr::new("stderr")) && count_linenumbers(&p) > LENGTH_LIMIT {
                Some(p)
            } else {
                None
            }
        })
        .collect()
}

#[must_use]
fn count_linenumbers(filepath: &Path) -> usize {
    match fs::read(filepath) {
        Ok(content) => bytecount::count(&content, b'\n'),
        Err(e) => {
            eprintln!("Failed to read file: {}", e);
            0
        },
    }
}
<commit_msg>Fix limit-stderr-files test<commit_after>use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};

use walkdir::WalkDir;

use clippy_dev::clippy_project_root;

// The maximum length allowed for stderr files.
//
// We limit this because small files are easier to deal with than bigger files.
const LENGTH_LIMIT: usize = 200;

pub fn check() {
    let exceeding_files: Vec<_> = exceeding_stderr_files();

    if !exceeding_files.is_empty() {
        eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT);
        for (path, count) in exceeding_files {
            println!("{}: {}", path.display(), count);
        }
        std::process::exit(1);
    }
}

fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> {
    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
    WalkDir::new(clippy_project_root().join("tests/ui"))
        .into_iter()
        .filter_map(Result::ok)
        .filter(|f| !f.file_type().is_dir())
        .filter_map(|e| {
            let p = e.into_path();
            let count = count_linenumbers(&p);
            if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT {
                Some((p, count))
            } else {
                None
            }
        })
        .collect()
}

#[must_use]
fn count_linenumbers(filepath: &Path) -> usize {
    match fs::read(filepath) {
        Ok(content) => bytecount::count(&content, b'\n'),
        Err(e) => {
            eprintln!("Failed to read file: {}", e);
            0
        },
    }
}
<|endoftext|>


<commit_before>//! lint on using `x.get(x.len() - 1)` instead of `x.last()`

use crate::utils::{match_type, paths, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
use if_chain::if_chain;
use rustc::hir::{BinOpKind, Expr, ExprKind};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use syntax::ast::LitKind;
use syntax::source_map::Spanned;
use syntax::symbol::Symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of
    /// `x.last()`.
    ///
    /// **Why is this bad?** Using `x.last()` is easier to read and has the same
    /// result.
    ///
    /// Note that using `x[x.len() - 1]` is semantically different from
    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
    /// accesses, while `x.get()` and `x.last()` will return `None`.
    ///
    /// There is another lint (get_unwrap) that covers the case of using
    /// `x.get(index).unwrap()` instead of `x[index]`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// // Bad
    /// let x = vec![2, 3, 5];
    /// let last_element = x.get(x.len() - 1);
    ///
    /// // Good
    /// let x = vec![2, 3, 5];
    /// let last_element = x.last();
    /// ```
    pub GET_LAST_WITH_LEN,
    complexity,
    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
}

declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for GetLastWithLen {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_chain! {
            // Is a method call
            if let ExprKind::MethodCall(ref path, _, ref args) = expr.node;

            // Method name is "get"
            if path.ident.name == Symbol::intern("get");

            // Argument 0 (the struct we're calling the method on) is a vector
            if let Some(struct_calling_on) = args.get(0);
            let struct_ty = cx.tables.expr_ty(struct_calling_on);
            if match_type(cx, struct_ty, &paths::VEC);

            // Argument to "get" is a subtraction
            if let Some(get_index_arg) = args.get(1);
            if let ExprKind::Binary(Spanned{node: BinOpKind::Sub, ..},
                                    lhs, rhs) = &get_index_arg.node;

            // LHS of subtraction is "x.len()"
            if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args) = &lhs.node;
            if arg_lhs_path.ident.name == Symbol::intern("len");
            if let Some(arg_lhs_struct) = lhs_args.get(0);

            // The two vectors referenced (x in x.get(...) and in x.len())
            if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct);

            // RHS of subtraction is 1
            if let ExprKind::Lit(rhs_lit) = &rhs.node;
            if let LitKind::Int(rhs_value, ..) = rhs_lit.node;
            if rhs_value == 1;

            then {
                let mut applicability = Applicability::MachineApplicable;
                let vec_name = snippet_with_applicability(
                    cx,
                    struct_calling_on.span, "vec",
                    &mut applicability,
                );

                span_lint_and_sugg(
                    cx,
                    GET_LAST_WITH_LEN,
                    expr.span,
                    &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name),
                    "try",
                    format!("{}.last()", vec_name),
                    applicability);
            }
        }
    }
}
<commit_msg>Formatting inside if_chain! macro<commit_after>//! lint on using `x.get(x.len() - 1)` instead of `x.last()`

use crate::utils::{match_type, paths, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
use if_chain::if_chain;
use rustc::hir::{BinOpKind, Expr, ExprKind};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use syntax::ast::LitKind;
use syntax::source_map::Spanned;
use syntax::symbol::Symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of
    /// `x.last()`.
    ///
    /// **Why is this bad?** Using `x.last()` is easier to read and has the same
    /// result.
    ///
    /// Note that using `x[x.len() - 1]` is semantically different from
    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
    /// accesses, while `x.get()` and `x.last()` will return `None`.
    ///
    /// There is another lint (get_unwrap) that covers the case of using
    /// `x.get(index).unwrap()` instead of `x[index]`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// // Bad
    /// let x = vec![2, 3, 5];
    /// let last_element = x.get(x.len() - 1);
    ///
    /// // Good
    /// let x = vec![2, 3, 5];
    /// let last_element = x.last();
    /// ```
    pub GET_LAST_WITH_LEN,
    complexity,
    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
}

declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for GetLastWithLen {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_chain! {
            // Is a method call
            if let ExprKind::MethodCall(ref path, _, ref args) = expr.node;

            // Method name is "get"
            if path.ident.name == Symbol::intern("get");

            // Argument 0 (the struct we're calling the method on) is a vector
            if let Some(struct_calling_on) = args.get(0);
            let struct_ty = cx.tables.expr_ty(struct_calling_on);
            if match_type(cx, struct_ty, &paths::VEC);

            // Argument to "get" is a subtraction
            if let Some(get_index_arg) = args.get(1);
            if let ExprKind::Binary(
                Spanned {
                    node: BinOpKind::Sub,
                    ..
                },
                lhs,
                rhs,
            ) = &get_index_arg.node;

            // LHS of subtraction is "x.len()"
            if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args) = &lhs.node;
            if arg_lhs_path.ident.name == Symbol::intern("len");
            if let Some(arg_lhs_struct) = lhs_args.get(0);

            // The two vectors referenced (x in x.get(...) and in x.len())
            if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct);

            // RHS of subtraction is 1
            if let ExprKind::Lit(rhs_lit) = &rhs.node;
            if let LitKind::Int(rhs_value, ..) = rhs_lit.node;
            if rhs_value == 1;

            then {
                let mut applicability = Applicability::MachineApplicable;
                let vec_name = snippet_with_applicability(
                    cx,
                    struct_calling_on.span, "vec",
                    &mut applicability,
                );

                span_lint_and_sugg(
                    cx,
                    GET_LAST_WITH_LEN,
                    expr.span,
                    &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name),
                    "try",
                    format!("{}.last()", vec_name),
                    applicability,
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>use rustc::hir::*;
use rustc::lint::*;
use utils::{SpanlessEq, span_lint, span_lint_and_then, multispan_sugg, snippet, implements_trait};
use utils::sugg::Sugg;

/// **What it does:** Checks for equal operands to comparison, logical and
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
/// `||`, `&`, `|`, `^`, `-` and `/`).
///
/// **Why is this bad?** This is usually just a typo or a copy and paste error.
///
/// **Known problems:** False negatives: We had some false positives regarding
/// calls (notably [racer](https://github.com/phildawes/racer) had one instance
/// of `x.pop() && x.pop()`), so we removed matching any function or method
/// calls. We may introduce a whitelist of known pure functions in the future.
///
/// **Example:**
/// ```rust
/// x + 1 == x + 1
/// ```
declare_lint! {
    pub EQ_OP,
    Warn,
    "equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
}

/// **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound
/// and suggests to dereference the other argument instead
///
/// **Why is this bad?** It is more idiomatic to dereference the other argument.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// &x == y
/// ```
declare_lint! {
    pub OP_REF,
    Warn,
    "taking a reference to satisfy the type constraints on `==`"
}

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

impl LintPass for EqOp {
    fn get_lints(&self) -> LintArray {
        lint_array!(EQ_OP, OP_REF)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprBinary(ref op, ref left, ref right) = e.node {
            if is_valid_operator(op) {
                if SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
                    span_lint(cx,
                            EQ_OP,
                            e.span,
                            &format!("equal expressions as operands to `{}`", op.node.as_str()));
                } else {
                    let trait_id = match op.node {
                        BiAdd => cx.tcx.lang_items.add_trait(),
                        BiSub => cx.tcx.lang_items.sub_trait(),
                        BiMul => cx.tcx.lang_items.mul_trait(),
                        BiDiv => cx.tcx.lang_items.div_trait(),
                        BiRem => cx.tcx.lang_items.rem_trait(),
                        BiAnd => None,
                        BiOr => None,
                        BiBitXor => cx.tcx.lang_items.bitxor_trait(),
                        BiBitAnd => cx.tcx.lang_items.bitand_trait(),
                        BiBitOr => cx.tcx.lang_items.bitor_trait(),
                        BiShl => cx.tcx.lang_items.shl_trait(),
                        BiShr => cx.tcx.lang_items.shr_trait(),
                        BiNe |
                        BiEq => cx.tcx.lang_items.eq_trait(),
                        BiLt |
                        BiLe |
                        BiGe |
                        BiGt => cx.tcx.lang_items.ord_trait(),
                    };
                    if let Some(trait_id) = trait_id {
                        match (&left.node, &right.node) {
                            // do not suggest to dereference literals
                            (&ExprLit(..), _) |
                            (_, &ExprLit(..)) => {},
                            // &foo == &bar
                            (&ExprAddrOf(_, ref l), &ExprAddrOf(_, ref r)) => {
                                if implements_trait(cx, cx.tables.expr_ty(l), trait_id, &[cx.tables.expr_ty(r)], None) {
                                    span_lint_and_then(cx,
                                        OP_REF,
                                        e.span,
                                        "taken reference of both operands, which is done automatically by the operator anyway",
                                        |db| {
                                            let lsnip = snippet(cx, l.span, "...").to_string();
                                            let rsnip = snippet(cx, r.span, "...").to_string();
                                            multispan_sugg(db,
                                                        "use the values directly".to_string(),
                                                        vec![(left.span, lsnip),
                                                            (right.span, rsnip)]);
                                        }
                                    )
                                }
                            }
                            // &foo == bar
                            (&ExprAddrOf(_, ref l), _) => {
                                if implements_trait(cx, cx.tables.expr_ty(l), trait_id, &[cx.tables.expr_ty(right)], None) {
                                    span_lint_and_then(cx,
                                        OP_REF,
                                        e.span,
                                        "taken reference of left operand",
                                        |db| {
                                            let lsnip = snippet(cx, l.span, "...").to_string();
                                            let rsnip = Sugg::hir(cx, right, "...").deref().to_string();
                                            multispan_sugg(db,
                                                        "dereference the right operand instead".to_string(),
                                                        vec![(left.span, lsnip),
                                                            (right.span, rsnip)]);
                                        }
                                    )
                                }
                            }
                            // foo == &bar
                            (_, &ExprAddrOf(_, ref r)) => {
                                if implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[cx.tables.expr_ty(r)], None) {
                                    span_lint_and_then(cx,
                                        OP_REF,
                                        e.span,
                                        "taken reference of right operand",
                                        |db| {
                                            let lsnip = Sugg::hir(cx, left, "...").deref().to_string();
                                            let rsnip = snippet(cx, r.span, "...").to_string();
                                            multispan_sugg(db,
                                                        "dereference the left operand instead".to_string(),
                                                        vec![(left.span, lsnip),
                                                            (right.span, rsnip)]);
                                        }
                                    )
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }
    }
}

fn is_valid_operator(op: &BinOp) -> bool {
    match op.node {
        BiSub | BiDiv | BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr | BiBitXor | BiBitAnd | BiBitOr => true,
        _ => false,
    }
}
<commit_msg>Dogfood<commit_after>use rustc::hir::*;
use rustc::lint::*;
use utils::{SpanlessEq, span_lint, span_lint_and_then, multispan_sugg, snippet, implements_trait};
use utils::sugg::Sugg;

/// **What it does:** Checks for equal operands to comparison, logical and
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
/// `||`, `&`, `|`, `^`, `-` and `/`).
///
/// **Why is this bad?** This is usually just a typo or a copy and paste error.
///
/// **Known problems:** False negatives: We had some false positives regarding
/// calls (notably [racer](https://github.com/phildawes/racer) had one instance
/// of `x.pop() && x.pop()`), so we removed matching any function or method
/// calls. We may introduce a whitelist of known pure functions in the future.
///
/// **Example:**
/// ```rust
/// x + 1 == x + 1
/// ```
declare_lint! {
    pub EQ_OP,
    Warn,
    "equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
}

/// **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound
/// and suggests to dereference the other argument instead
///
/// **Why is this bad?** It is more idiomatic to dereference the other argument.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// &x == y
/// ```
declare_lint! {
    pub OP_REF,
    Warn,
    "taking a reference to satisfy the type constraints on `==`"
}

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

impl LintPass for EqOp {
    fn get_lints(&self) -> LintArray {
        lint_array!(EQ_OP, OP_REF)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprBinary(ref op, ref left, ref right) = e.node {
            if is_valid_operator(op) {
                if SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
                    span_lint(cx,
                            EQ_OP,
                            e.span,
                            &format!("equal expressions as operands to `{}`", op.node.as_str()));
                } else {
                    let trait_id = match op.node {
                        BiAdd => cx.tcx.lang_items.add_trait(),
                        BiSub => cx.tcx.lang_items.sub_trait(),
                        BiMul => cx.tcx.lang_items.mul_trait(),
                        BiDiv => cx.tcx.lang_items.div_trait(),
                        BiRem => cx.tcx.lang_items.rem_trait(),
                        BiAnd |
                        BiOr => None,
                        BiBitXor => cx.tcx.lang_items.bitxor_trait(),
                        BiBitAnd => cx.tcx.lang_items.bitand_trait(),
                        BiBitOr => cx.tcx.lang_items.bitor_trait(),
                        BiShl => cx.tcx.lang_items.shl_trait(),
                        BiShr => cx.tcx.lang_items.shr_trait(),
                        BiNe |
                        BiEq => cx.tcx.lang_items.eq_trait(),
                        BiLt |
                        BiLe |
                        BiGe |
                        BiGt => cx.tcx.lang_items.ord_trait(),
                    };
                    if let Some(trait_id) = trait_id {
                        #[allow(match_same_arms)]
                        match (&left.node, &right.node) {
                            // do not suggest to dereference literals
                            (&ExprLit(..), _) |
                            (_, &ExprLit(..)) => {},
                            // &foo == &bar
                            (&ExprAddrOf(_, ref l), &ExprAddrOf(_, ref r)) => {
                                if implements_trait(cx, cx.tables.expr_ty(l), trait_id, &[cx.tables.expr_ty(r)], None) {
                                    span_lint_and_then(cx,
                                        OP_REF,
                                        e.span,
                                        "taken reference of both operands, which is done automatically by the operator anyway",
                                        |db| {
                                            let lsnip = snippet(cx, l.span, "...").to_string();
                                            let rsnip = snippet(cx, r.span, "...").to_string();
                                            multispan_sugg(db,
                                                        "use the values directly".to_string(),
                                                        vec![(left.span, lsnip),
                                                            (right.span, rsnip)]);
                                        }
                                    )
                                }
                            }
                            // &foo == bar
                            (&ExprAddrOf(_, ref l), _) => {
                                if implements_trait(cx, cx.tables.expr_ty(l), trait_id, &[cx.tables.expr_ty(right)], None) {
                                    span_lint_and_then(cx,
                                        OP_REF,
                                        e.span,
                                        "taken reference of left operand",
                                        |db| {
                                            let lsnip = snippet(cx, l.span, "...").to_string();
                                            let rsnip = Sugg::hir(cx, right, "...").deref().to_string();
                                            multispan_sugg(db,
                                                        "dereference the right operand instead".to_string(),
                                                        vec![(left.span, lsnip),
                                                            (right.span, rsnip)]);
                                        }
                                    )
                                }
                            }
                            // foo == &bar
                            (_, &ExprAddrOf(_, ref r)) => {
                                if implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[cx.tables.expr_ty(r)], None) {
                                    span_lint_and_then(cx,
                                        OP_REF,
                                        e.span,
                                        "taken reference of right operand",
                                        |db| {
                                            let lsnip = Sugg::hir(cx, left, "...").deref().to_string();
                                            let rsnip = snippet(cx, r.span, "...").to_string();
                                            multispan_sugg(db,
                                                        "dereference the left operand instead".to_string(),
                                                        vec![(left.span, lsnip),
                                                            (right.span, rsnip)]);
                                        }
                                    )
                                }
                            }
                            _ => {}
                        }
                    }
                }
            }
        }
    }
}

fn is_valid_operator(op: &BinOp) -> bool {
    match op.node {
        BiSub | BiDiv | BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr | BiBitXor | BiBitAnd | BiBitOr => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use specs::*;
use util::*;

use amq_protocol_types::AMQPType;
use handlebars::{self, Context, Handlebars, Helper, HelperDef, HelperResult, JsonValue, Output, Renderable, RenderContext, RenderError, ScopedJson, to_json};
use serde_json::{self};

use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::collections::BTreeMap;

/// Type alias to avoid making our users explicitely depend on an extra dependency
pub type CodeGenerator = Handlebars;

/// Our extension for better integration with Handlebars
pub trait HandlebarsAMQPExtension {
    /// Register the various standrad helpers we'll need for AMQP codegen
    fn register_amqp_helpers(self) -> Self;
    /// Generate code using the standard representation of specs and the given template, using the
    /// given name for the variable holding the [protocol definition](../specs.AMQProtocolDefinition.html).
    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str);
}

impl HandlebarsAMQPExtension for CodeGenerator {
    fn register_amqp_helpers(mut self) -> CodeGenerator {
        self.register_escape_fn(handlebars::no_escape);
        self.register_helper("camel",            Box::new(CamelHelper));
        self.register_helper("snake",            Box::new(SnakeHelper));
        self.register_helper("snake_type",       Box::new(SnakeTypeHelper));
        self.register_helper("sanitize_name",    Box::new(SanitizeNameHelper));
        self.register_helper("method_has_flags", Box::new(MethodHasFlagsHelper));
        self.register_helper("each_argument",    Box::new(EachArgumentHelper));
        self.register_helper("each_flag",        Box::new(EachFlagHelper));
        self
    }

    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str) {
        let dest_path   = Path::new(out_dir).join(format!("{}.rs", target));
        let mut f       = File::create(&dest_path).unwrap_or_else(|_| panic!("Failed to create {}.rs", target));
        let specs       = AMQProtocolDefinition::load();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        let mut data    = BTreeMap::new();

        codegen.set_strict_mode(true);
        codegen.register_template_string(template_name, template.to_string()).unwrap_or_else(|_| panic!("Failed to register {} template", template_name));
        data.insert(var_name.to_string(), specs);

        writeln!(f, "{}", codegen.render(template_name, &data).unwrap_or_else(|err| panic!("Failed to render {} template: {}", template_name, err))).unwrap_or_else(|_| panic!("Failed to generate {}.rs", target));
    }
}

/// Helper for converting text to camel case
pub struct CamelHelper;
impl HelperDef for CamelHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"camel\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"camel\""))?;
        out.write(&camel_case(param))?;
        Ok(())
    }
}

/// Helper for converting text to snake case
pub struct SnakeHelper;
impl HelperDef for SnakeHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"snake\""))?;
        out.write(&snake_case(param))?;
        Ok(())
    }
}

/// Helper for getting the type name converted to snake case
pub struct SnakeTypeHelper;
impl HelperDef for SnakeTypeHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value           = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
        let param: AMQPType = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not an AMQPType for helper \"snake_type\""))?;
        out.write(&snake_case(&param.to_string()))?;
        Ok(())
    }
}

/// Helper to sanitize name so the it becomes a valid identifier
pub struct SanitizeNameHelper;
impl HelperDef for SanitizeNameHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"sanitize_name\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"sanitize_name\""))?;
        out.write(&param.replace('-', "_"))?;
        Ok(())
    }
}

/// Helper for checking is a method has some flags argument
pub struct MethodHasFlagsHelper;
impl HelperDef for MethodHasFlagsHelper {
    fn call_inner<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        let value     = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"method_has_flags\""))?;
        let method    = serde_json::from_value::<AMQPMethod>(value.value().clone()).map_err(|_| RenderError::new("Non-AMQPMethod param given to helper \"camel\""))?;
        let has_flags = method.arguments.iter().any(|arg| match *arg {
            AMQPArgument::Value(_) => false,
            AMQPArgument::Flags(_) => true,
        });
        Ok(Some(ScopedJson::Derived(JsonValue::from(has_flags))))
    }
}

/// Helper to walk through a Vec of [AMQPArgument](../specs.AMQPArgument.html).
pub struct EachArgumentHelper;
impl HelperDef for EachArgumentHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars, ctx: &'rc Context, rc: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_argument\""))?;

        if let Some(t) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
            let arguments : Vec<AMQPArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPArgument> for helper \"each_argument\""))?;
            for (index, argument) in arguments.iter().enumerate() {
                let mut local_rc = rc.derive();
                if let Some(ref p) = local_path_root {
                    local_rc.push_local_path_root(p.clone());
                }
                local_rc.set_local_var("@index".to_string(), to_json(&index));
                if let Some(inner_path) = value.path() {
                    let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                    local_rc.set_path(new_path.clone());
                }
                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    match *argument {
                        AMQPArgument::Value(ref v) => {
                            map.insert(block_param.to_string(), to_json(v));
                            map.insert("argument_is_value".to_string(), to_json(&true));
                        },
                        AMQPArgument::Flags(ref f) => {
                            map.insert(block_param.to_string(), to_json(f));
                            map.insert("argument_is_value".to_string(), to_json(&false));
                        },
                    };
                    local_rc.push_block_context(&map)?;
                }
                t.render(r, ctx, &mut local_rc, out)?;
                if h.block_param().is_some() {
                    local_rc.pop_block_context();
                }
                if local_path_root.is_some() {
                    local_rc.pop_local_path_root();
                }
            }
            rc.demote_local_vars();
        }
        Ok(())
    }
}

/// Helper to walk through a Vec of [AMQPFlagArgument](../specs.AMQPFlagArgument.html).
pub struct EachFlagHelper;
impl HelperDef for EachFlagHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars, ctx: &'rc Context, rc: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_flag\""))?;

        if let Some(t) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
            let flags : Vec<AMQPFlagArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPFlagArgument> for helper \"each_flag\""))?;
            for (index, flag) in flags.iter().enumerate() {
                let mut local_rc = rc.derive();
                if let Some(ref p) = local_path_root {
                    local_rc.push_local_path_root(p.clone());
                }
                local_rc.set_local_var("@index".to_string(), to_json(&index));
                if let Some(inner_path) = value.path() {
                    let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                    local_rc.set_path(new_path.clone());
                }
                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    map.insert(block_param.to_string(), to_json(flag));
                    local_rc.push_block_context(&map)?;
                }
                t.render(r, ctx, &mut local_rc, out)?;
                if h.block_param().is_some() {
                    local_rc.pop_block_context();
                }
                if local_path_root.is_some() {
                    local_rc.pop_local_path_root();
                }
            }
            rc.demote_local_vars();
        }
        Ok(())
    }
}

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

    use amq_protocol_types::*;

    use std::collections::BTreeMap;

    pub const TEMPLATE: &'static str = r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{protocol.copyright}}
port {{protocol.port}}
{{#each protocol.domains ~}}
{{@key}}: {{this}}
{{/each ~}}
{{#each protocol.constants as |constant| ~}}
{{constant.name}} = {{constant.value}}
{{/each ~}}
{{#each protocol.classes as |class| ~}}
{{class.id}} - {{class.name}}
{{#each class.properties as |property| ~}}
{{property.name}}: {{property.type}}
{{/each ~}}
{{#each class.methods as |method| ~}}
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{#each_argument method.arguments as |argument| ~}}
{{#if argument_is_value ~}}
{{argument.name}}({{argument.domain}}): {{argument.type}}
{{else}}
{{#each_flag argument as |flag| ~}}
{{flag.name}}: {{flag.default_value}}
{{/each_flag ~}}
{{/if ~}}
{{/each_argument ~}}
{{/each ~}}
{{/each ~}}
"#;

    fn specs() -> AMQProtocolDefinition {
        let mut domains = BTreeMap::new();
        domains.insert("domain1".to_string(), AMQPType::LongString);
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     "Copyright 1\nCopyright 2".to_string(),
            domains:       domains,
            constants:     vec![
                AMQPConstant {
                    name:      "constant1".to_string(),
                    amqp_type: AMQPType::ShortUInt,
                    value:     128,
                }
            ],
            soft_errors:   Vec::new(),
            hard_errors:   Vec::new(),
            classes:       vec![
                AMQPClass {
                    id:             42,
                    methods:        vec![
                        AMQPMethod {
                            id:            64,
                            arguments:     vec![
                                AMQPArgument::Value(AMQPValueArgument {
                                    amqp_type:     AMQPType::LongString,
                                    name:          "argument1".to_string(),
                                    default_value: Some(AMQPValue::LongString("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }),
                                AMQPArgument::Flags(vec![
                                    AMQPFlagArgument {
                                        name:         "flag1".to_string(),
                                        default_value: true,
                                    },
                                    AMQPFlagArgument {
                                        name:         "flag2".to_string(),
                                        default_value: false,
                                    },
                                ]),
                            ],
                            name:          "method1".to_string(),
                            synchronous:   true,
                        }
                    ],
                    name:           "class1".to_string(),
                    properties:     vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ],
                }
            ],
        }
    }

    #[test]
    fn main_template() {
        let mut data    = BTreeMap::new();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        data.insert("protocol".to_string(), specs());
        assert!(codegen.register_template_string("main", TEMPLATE.to_string()).is_ok());
        assert_eq!(codegen.render("main", &data).unwrap(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: LongString
constant1 = 128
42 - class1
property1: LongString
64 - method1
synchronous: true
argument1(domain1): LongString

flag1: true
flag2: false

"#);
    }
}
<commit_msg>codegen: fix tests with handlbars update<commit_after>use specs::*;
use util::*;

use amq_protocol_types::AMQPType;
use handlebars::{self, Context, Handlebars, Helper, HelperDef, HelperResult, JsonValue, Output, Renderable, RenderContext, RenderError, ScopedJson, to_json};
use serde_json::{self};

use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::collections::BTreeMap;

/// Type alias to avoid making our users explicitely depend on an extra dependency
pub type CodeGenerator = Handlebars;

/// Our extension for better integration with Handlebars
pub trait HandlebarsAMQPExtension {
    /// Register the various standrad helpers we'll need for AMQP codegen
    fn register_amqp_helpers(self) -> Self;
    /// Generate code using the standard representation of specs and the given template, using the
    /// given name for the variable holding the [protocol definition](../specs.AMQProtocolDefinition.html).
    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str);
}

impl HandlebarsAMQPExtension for CodeGenerator {
    fn register_amqp_helpers(mut self) -> CodeGenerator {
        self.register_escape_fn(handlebars::no_escape);
        self.register_helper("camel",            Box::new(CamelHelper));
        self.register_helper("snake",            Box::new(SnakeHelper));
        self.register_helper("snake_type",       Box::new(SnakeTypeHelper));
        self.register_helper("sanitize_name",    Box::new(SanitizeNameHelper));
        self.register_helper("method_has_flags", Box::new(MethodHasFlagsHelper));
        self.register_helper("each_argument",    Box::new(EachArgumentHelper));
        self.register_helper("each_flag",        Box::new(EachFlagHelper));
        self
    }

    fn simple_codegen(out_dir: &str, target: &str, template_name: &str, template: &str, var_name: &str) {
        let dest_path   = Path::new(out_dir).join(format!("{}.rs", target));
        let mut f       = File::create(&dest_path).unwrap_or_else(|_| panic!("Failed to create {}.rs", target));
        let specs       = AMQProtocolDefinition::load();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        let mut data    = BTreeMap::new();

        codegen.set_strict_mode(true);
        codegen.register_template_string(template_name, template.to_string()).unwrap_or_else(|_| panic!("Failed to register {} template", template_name));
        data.insert(var_name.to_string(), specs);

        writeln!(f, "{}", codegen.render(template_name, &data).unwrap_or_else(|err| panic!("Failed to render {} template: {}", template_name, err))).unwrap_or_else(|_| panic!("Failed to generate {}.rs", target));
    }
}

/// Helper for converting text to camel case
pub struct CamelHelper;
impl HelperDef for CamelHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"camel\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"camel\""))?;
        out.write(&camel_case(param))?;
        Ok(())
    }
}

/// Helper for converting text to snake case
pub struct SnakeHelper;
impl HelperDef for SnakeHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"snake\""))?;
        out.write(&snake_case(param))?;
        Ok(())
    }
}

/// Helper for getting the type name converted to snake case
pub struct SnakeTypeHelper;
impl HelperDef for SnakeTypeHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value           = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
        let param: AMQPType = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not an AMQPType for helper \"snake_type\""))?;
        out.write(&snake_case(&param.to_string()))?;
        Ok(())
    }
}

/// Helper to sanitize name so the it becomes a valid identifier
pub struct SanitizeNameHelper;
impl HelperDef for SanitizeNameHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"sanitize_name\""))?;
        let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"sanitize_name\""))?;
        out.write(&param.replace('-', "_"))?;
        Ok(())
    }
}

/// Helper for checking is a method has some flags argument
pub struct MethodHasFlagsHelper;
impl HelperDef for MethodHasFlagsHelper {
    fn call_inner<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, _: &'reg Handlebars, _: &'rc Context, _: &mut RenderContext<'reg>) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        let value     = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"method_has_flags\""))?;
        let method    = serde_json::from_value::<AMQPMethod>(value.value().clone()).map_err(|_| RenderError::new("Non-AMQPMethod param given to helper \"camel\""))?;
        let has_flags = method.arguments.iter().any(|arg| match *arg {
            AMQPArgument::Value(_) => false,
            AMQPArgument::Flags(_) => true,
        });
        Ok(Some(ScopedJson::Derived(JsonValue::from(has_flags))))
    }
}

/// Helper to walk through a Vec of [AMQPArgument](../specs.AMQPArgument.html).
pub struct EachArgumentHelper;
impl HelperDef for EachArgumentHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars, ctx: &'rc Context, rc: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_argument\""))?;

        if let Some(t) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
            let arguments : Vec<AMQPArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPArgument> for helper \"each_argument\""))?;
            for (index, argument) in arguments.iter().enumerate() {
                let mut local_rc = rc.derive();
                if let Some(ref p) = local_path_root {
                    local_rc.push_local_path_root(p.clone());
                }
                local_rc.set_local_var("@index".to_string(), to_json(&index));
                if let Some(inner_path) = value.path() {
                    let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                    local_rc.set_path(new_path.clone());
                }
                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    match *argument {
                        AMQPArgument::Value(ref v) => {
                            map.insert(block_param.to_string(), to_json(v));
                            map.insert("argument_is_value".to_string(), to_json(&true));
                        },
                        AMQPArgument::Flags(ref f) => {
                            map.insert(block_param.to_string(), to_json(f));
                            map.insert("argument_is_value".to_string(), to_json(&false));
                        },
                    };
                    local_rc.push_block_context(&map)?;
                }
                t.render(r, ctx, &mut local_rc, out)?;
                if h.block_param().is_some() {
                    local_rc.pop_block_context();
                }
                if local_path_root.is_some() {
                    local_rc.pop_local_path_root();
                }
            }
            rc.demote_local_vars();
        }
        Ok(())
    }
}

/// Helper to walk through a Vec of [AMQPFlagArgument](../specs.AMQPFlagArgument.html).
pub struct EachFlagHelper;
impl HelperDef for EachFlagHelper {
    fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars, ctx: &'rc Context, rc: &mut RenderContext<'reg>, out: &mut Output) -> HelperResult {
        let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_flag\""))?;

        if let Some(t) = h.template() {
            rc.promote_local_vars();
            let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
            let flags : Vec<AMQPFlagArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPFlagArgument> for helper \"each_flag\""))?;
            for (index, flag) in flags.iter().enumerate() {
                let mut local_rc = rc.derive();
                if let Some(ref p) = local_path_root {
                    local_rc.push_local_path_root(p.clone());
                }
                local_rc.set_local_var("@index".to_string(), to_json(&index));
                if let Some(inner_path) = value.path() {
                    let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                    local_rc.set_path(new_path.clone());
                }
                if let Some(block_param) = h.block_param() {
                    let mut map = BTreeMap::new();
                    map.insert(block_param.to_string(), to_json(flag));
                    local_rc.push_block_context(&map)?;
                }
                t.render(r, ctx, &mut local_rc, out)?;
                if h.block_param().is_some() {
                    local_rc.pop_block_context();
                }
                if local_path_root.is_some() {
                    local_rc.pop_local_path_root();
                }
            }
            rc.demote_local_vars();
        }
        Ok(())
    }
}

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

    use amq_protocol_types::*;

    use std::collections::BTreeMap;

    pub const TEMPLATE: &'static str = r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{protocol.copyright}}
port {{protocol.port}}
{{#each protocol.domains ~}}
{{@key}}: {{this}}
{{/each ~}}
{{#each protocol.constants as |constant| ~}}
{{constant.name}} = {{constant.value}}
{{/each ~}}
{{#each protocol.classes as |class| ~}}
{{class.id}} - {{class.name}}
{{#each class.properties as |property| ~}}
{{property.name}}: {{property.type}}
{{/each ~}}
{{#each class.methods as |method| ~}}
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{#each_argument method.arguments as |argument| ~}}
{{#if argument_is_value ~}}
{{argument.name}}({{argument.domain}}): {{argument.type}}
{{else}}
{{#each_flag argument as |flag| ~}}
{{flag.name}}: {{flag.default_value}}
{{/each_flag ~}}
{{/if ~}}
{{/each_argument ~}}
{{/each ~}}
{{/each ~}}
"#;

    fn specs() -> AMQProtocolDefinition {
        let mut domains = BTreeMap::new();
        domains.insert("domain1".to_string(), AMQPType::LongString);
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     "Copyright 1\nCopyright 2".to_string(),
            domains:       domains,
            constants:     vec![
                AMQPConstant {
                    name:      "constant1".to_string(),
                    amqp_type: AMQPType::ShortUInt,
                    value:     128,
                }
            ],
            soft_errors:   Vec::new(),
            hard_errors:   Vec::new(),
            classes:       vec![
                AMQPClass {
                    id:             42,
                    methods:        vec![
                        AMQPMethod {
                            id:            64,
                            arguments:     vec![
                                AMQPArgument::Value(AMQPValueArgument {
                                    amqp_type:     AMQPType::LongString,
                                    name:          "argument1".to_string(),
                                    default_value: Some(AMQPValue::LongString("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }),
                                AMQPArgument::Flags(vec![
                                    AMQPFlagArgument {
                                        name:         "flag1".to_string(),
                                        default_value: true,
                                    },
                                    AMQPFlagArgument {
                                        name:         "flag2".to_string(),
                                        default_value: false,
                                    },
                                ]),
                            ],
                            name:          "method1".to_string(),
                            synchronous:   true,
                        }
                    ],
                    name:           "class1".to_string(),
                    properties:     vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ],
                }
            ],
        }
    }

    #[test]
    fn main_template() {
        let mut data    = BTreeMap::new();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        data.insert("protocol".to_string(), specs());
        assert!(codegen.register_template_string("main", TEMPLATE.to_string()).is_ok());
        assert_eq!(codegen.render("main", &data).unwrap(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: LongString
constant1 = 128
42 - class1
property1: LongString
64 - method1
synchronous: true
argument1(domain1): LongString

flag1: true
flag2: false
"#);
    }
}
<|endoftext|>


<commit_before>use cgmath::Point2;
use game::constants::{TERRAIN_OBJECTS, TILES_PCS_H, TILES_PCS_W};
use game::get_rand_from_range;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn neighbours(curr_pos: Point2<i32>) -> Vec<Point2<i32>> {
  vec![Point2::new(curr_pos.x - 1, curr_pos.y),
       Point2::new(curr_pos.x - 1, curr_pos.y - 1),
       Point2::new(curr_pos.x, curr_pos.y - 1),
       Point2::new(curr_pos.x + 1, curr_pos.y),
       Point2::new(curr_pos.x + 1, curr_pos.y + 1),
       Point2::new(curr_pos.x, curr_pos.y + 1),
       Point2::new(curr_pos.x - 1, curr_pos.y + 1),
       Point2::new(curr_pos.x + 1, curr_pos.y - 1)
  ]
}

fn tiles(p: &Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Vec<(Point2<i32>, i32)> {
  neighbours(Point2::new(p.x as i32, p.y as i32)).iter()
                                                 .filter(|ref e| e.x >= 0 && e.x < TILES_PCS_W as i32 && e.y >= 0 && e.y < TILES_PCS_H as i32)
                                                 .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
                                                 .map(|p| (Point2::new(p.x, p.y), 1))
                                                 .collect()
}

fn find_next_best_endpoint(end_point: Point2<i32>) -> Point2<i32> {
  let player_and_its_neighbour_tiles: Vec<[usize; 2]> = neighbours(end_point)
    .iter()
    .map(|p| [p.x as usize, p.y as usize])
    .collect();

  if player_and_its_neighbour_tiles.iter().any(|e| e[0] == end_point.x as usize && e[1] == end_point.y as usize) {
    let neighbours = tiles(&end_point, player_and_its_neighbour_tiles.as_slice());
    neighbours[0].0
  } else {
    end_point
  }
}

pub fn calc_route(start_point: Position, end_point: Position, impassable_tiles: &[[usize; 2]]) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = find_next_best_endpoint(coords_to_tile_offset(end_point));

  astar(&start,
        |p: &Point2<i32>| tiles(p, &impassable_tiles),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point, &TERRAIN_OBJECTS)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);
  let diff: (i32, i32) = (next_step.x - start.x, next_step.y - start.y);

  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<commit_msg>Refactor "neighbours" function<commit_after>use cgmath::Point2;
use game::constants::{TERRAIN_OBJECTS, TILES_PCS_H, TILES_PCS_W};
use game::get_rand_from_range;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn neighbours<'c>(curr_pos: &'c Point2<i32>, impassable_tiles: &[[usize; 2]], neighbours: &'c mut Vec<Point2<i32>>) -> Vec<&'c Point2<i32>> {
  neighbours.push(Point2::new(curr_pos.x - 1, curr_pos.y));
  neighbours.push(Point2::new(curr_pos.x - 1, curr_pos.y - 1));
  neighbours.push(Point2::new(curr_pos.x, curr_pos.y - 1));
  neighbours.push(Point2::new(curr_pos.x + 1, curr_pos.y));
  neighbours.push(Point2::new(curr_pos.x + 1, curr_pos.y + 1));
  neighbours.push(Point2::new(curr_pos.x, curr_pos.y + 1));
  neighbours.push(Point2::new(curr_pos.x - 1, curr_pos.y + 1));
  neighbours.push(Point2::new(curr_pos.x + 1, curr_pos.y - 1));

  neighbours.iter()
            .filter(|ref e| e.x >= 0 && e.x < TILES_PCS_W as i32 && e.y >= 0 && e.y < TILES_PCS_H as i32)
            .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
            .collect()
}

fn tiles(p: &Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Vec<(Point2<i32>, i32)> {
  neighbours(&Point2::new(p.x as i32, p.y as i32), &impassable_tiles, &mut vec![]).iter()
                                                                                .map(|p| (Point2::new(p.x, p.y), 1))
                                                                                .collect()
}

fn find_next_best_endpoint(end_point: Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Point2<i32> {
  let player_and_its_neighbour_tiles: Vec<[usize; 2]> = neighbours(&end_point, &impassable_tiles, &mut vec![])
    .iter()
    .map(|p| [p.x as usize, p.y as usize])
    .collect();

  if player_and_its_neighbour_tiles.iter().any(|e| e[0] == end_point.x as usize && e[1] == end_point.y as usize) {
    let neighbours = tiles(&end_point, player_and_its_neighbour_tiles.as_slice());
    neighbours[0].0
  } else {
    end_point
  }
}

pub fn calc_route(start_point: Position, end_point: Position, impassable_tiles: &[[usize; 2]]) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = find_next_best_endpoint(coords_to_tile_offset(end_point), &impassable_tiles);

  astar(&start,
        |p: &Point2<i32>| tiles(p, &impassable_tiles),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point, &TERRAIN_OBJECTS)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);
  let diff: (i32, i32) = (next_step.x - start.x, next_step.y - start.y);

  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<|endoftext|>


<commit_before>// xfail-pretty

use std;
use rustc;

import rustc::*;
import std::io::*;

import rustc::driver::diagnostic;
import rustc::syntax::ast;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::*;

fn new_parse_sess() -> parser::parse_sess {
    let cm = codemap::new_codemap();
    let handler = diagnostic::mk_handler(option::none);
    let sess = @{
        cm: cm,
        mutable next_id: 1,
        span_diagnostic: diagnostic::mk_span_handler(handler, cm),
        mutable chpos: 0u,
        mutable byte_pos: 0u
    };
    ret sess;
}

iface fake_ext_ctxt {
    fn session() -> fake_session;
}

type fake_options = {cfg: ast::crate_cfg};

type fake_session = {opts: @fake_options,
                     parse_sess: parser::parse_sess};

impl of fake_ext_ctxt for fake_session {
    fn session() -> fake_session {self}
}

fn mk_ctxt() -> fake_ext_ctxt {
    let opts : fake_options = {cfg: []};
    {opts: @opts, parse_sess: new_parse_sess()} as fake_ext_ctxt
}

fn main() {
    let ext_cx = mk_ctxt();

    let abc = #ast{23};
    check_pp(abc,  pprust::print_expr, "23");

    let expr3 = #ast{2 - $(abc) + 7};
    check_pp(expr3,  pprust::print_expr, "2 - 23 + 7");

    let expr4 = #ast{2 - $(#(3)) + 9};
    check_pp(expr4,  pprust::print_expr, "2 - 3 + 9");

    let ty = #ast(ty){option<int>};
    check_pp(ty, pprust::print_type, "option<int>");

    let item = #ast(item){const x : int = 10;};
    check_pp(item, pprust::print_item, "const x: int = 10;");

    let item2: @ast::item = #ast(item){const x : int = $(abc);};
    check_pp(item2, pprust::print_item, "const x: int = 23;");

    let stmt = #ast(stmt){let x = 20;};
    check_pp(*stmt, pprust::print_stmt, "let x = 20;");

    let stmt2 = #ast(stmt){let x : $(ty) = some($(abc));};
    check_pp(*stmt2, pprust::print_stmt, "let x: option<int> = some(23);");

    let pat = #ast(pat){some(_)};
    check_pp(pat, pprust::print_pat, "some(_)");
}

fn check_pp<T>(expr: T, f: fn(pprust::ps, T), expect: str) {
    let buf = mk_mem_buffer();
    let pp = pprust::rust_printer(buf as std::io::writer);
    f(pp, expr);
    pp::eof(pp.s);
    let str = mem_buffer_str(buf);
    stdout().write_line(str);
    if expect != "" {assert str == expect;}
}

<commit_msg>Additional testcase.<commit_after>// xfail-pretty

use std;
use rustc;

import rustc::*;
import std::io::*;

import rustc::driver::diagnostic;
import rustc::syntax::ast;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::*;

fn new_parse_sess() -> parser::parse_sess {
    let cm = codemap::new_codemap();
    let handler = diagnostic::mk_handler(option::none);
    let sess = @{
        cm: cm,
        mutable next_id: 1,
        span_diagnostic: diagnostic::mk_span_handler(handler, cm),
        mutable chpos: 0u,
        mutable byte_pos: 0u
    };
    ret sess;
}

iface fake_ext_ctxt {
    fn session() -> fake_session;
}

type fake_options = {cfg: ast::crate_cfg};

type fake_session = {opts: @fake_options,
                     parse_sess: parser::parse_sess};

impl of fake_ext_ctxt for fake_session {
    fn session() -> fake_session {self}
}

fn mk_ctxt() -> fake_ext_ctxt {
    let opts : fake_options = {cfg: []};
    {opts: @opts, parse_sess: new_parse_sess()} as fake_ext_ctxt
}

fn main() {
    let ext_cx = mk_ctxt();

    let abc = #ast{23};
    check_pp(abc,  pprust::print_expr, "23");

    let expr3 = #ast{2 - $(abc) + 7};
    check_pp(expr3,  pprust::print_expr, "2 - 23 + 7");

    let expr4 = #ast{2 - $(#(3)) + 9};
    check_pp(expr4,  pprust::print_expr, "2 - 3 + 9");

    let ty = #ast(ty){int};
    check_pp(ty, pprust::print_type, "int");

    let ty2 = #ast(ty){option<$(ty)>};
    check_pp(ty2, pprust::print_type, "option<int>");

    let item = #ast(item){const x : int = 10;};
    check_pp(item, pprust::print_item, "const x: int = 10;");

    let item2: @ast::item = #ast(item){const x : int = $(abc);};
    check_pp(item2, pprust::print_item, "const x: int = 23;");

    let stmt = #ast(stmt){let x = 20;};
    check_pp(*stmt, pprust::print_stmt, "let x = 20;");

    let stmt2 = #ast(stmt){let x : $(ty) = $(abc);};
    check_pp(*stmt2, pprust::print_stmt, "let x: int = 23;");

    let pat = #ast(pat){some(_)};
    check_pp(pat, pprust::print_pat, "some(_)");
}

fn check_pp<T>(expr: T, f: fn(pprust::ps, T), expect: str) {
    let buf = mk_mem_buffer();
    let pp = pprust::rust_printer(buf as std::io::writer);
    f(pp, expr);
    pp::eof(pp.s);
    let str = mem_buffer_str(buf);
    stdout().write_line(str);
    if expect != "" {assert str == expect;}
}

<|endoftext|>


<commit_before>#![crate_name = "uutest"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) mahkoh (ju.orth [at] gmail [dot] com)
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate libc;
extern crate num;

use std::os::{args_as_bytes};
use std::str::{from_utf8};
use num::bigint::{BigInt};

pub fn uumain(_: Vec<String>) -> int {
    let args = args_as_bytes();
    let args: Vec<&[u8]> = args.iter().map(|a| a.as_slice()).collect();
    let args = args.as_slice();
    if args.len() == 0 {
        return 2;
    }
    let args = match args[0] {
        b"[" => match args[args.len()-1] {
            b"]" => args.slice(1, args.len()-1),
            _ => return 2,
        },
        _ => args.slice(1, args.len()),
    };
    let rv = match args.len() {
        0 => false,
        1 => one(args),
        2 => two(args),
        3 => three(args),
        4 => four(args),
        _ => return 2,
    };
    1 - rv as int
}

fn one(args: &[&[u8]]) -> bool {
    args[0].len() > 0
}

fn two(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !one(args.slice_from(1)),
        b"-b" => path(args[1], BlockSpecial),
        b"-c" => path(args[1], CharacterSpecial),
        b"-d" => path(args[1], Directory),
        b"-e" => path(args[1], Exists),
        b"-f" => path(args[1], Regular),
        b"-g" => path(args[1], GroupIDFlag),
        b"-h" => path(args[1], SymLink),
        b"-L" => path(args[1], SymLink),
        b"-n" => one(args.slice_from(1)),
        b"-p" => path(args[1], FIFO),
        b"-r" => path(args[1], Readable),
        b"-S" => path(args[1], Socket),
        b"-s" => path(args[1], NonEmpty),
        b"-t" => isatty(args[1]),
        b"-u" => path(args[1], UserIDFlag),
        b"-w" => path(args[1], Writable),
        b"-x" => path(args[1], Executable),
        b"-z" => !one(args.slice_from(1)),
        _ => false,
    }
}

fn three(args: &[&[u8]]) -> bool {
    match args[1] {
        b"=" => args[0] == args[2],
        b"!=" => args[0] != args[2],
        b"-eq" => integers(args[0], args[2], Equal),
        b"-ne" => integers(args[0], args[2], Unequal),
        b"-gt" => integers(args[0], args[2], Greater),
        b"-ge" => integers(args[0], args[2], GreaterEqual),
        b"-lt" => integers(args[0], args[2], Less),
        b"-le" => integers(args[0], args[2], LessEqual),
        _ => match args[0] {
            b"!" => !two(args.slice_from(1)),
            _ => false,
        }
    }
}

fn four(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !three(args.slice_from(1)),
        _ => false,
    }
}

enum IntegerCondition {
    Equal,
    Unequal,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
}

fn integers(a: &[u8], b: &[u8], cond: IntegerCondition) -> bool {
    let (a, b): (&str, &str) = match (from_utf8(a), from_utf8(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    let (a, b): (BigInt, BigInt) = match (from_str(a), from_str(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    match cond {
        Equal        => a == b,
        Unequal      => a != b,
        Greater      => a >  b,
        GreaterEqual => a >= b,
        Less         => a <  b,
        LessEqual    => a <= b,
    }
}

fn isatty(fd: &[u8]) -> bool {
    use libc::{isatty};
    from_utf8(fd).and_then(|s| from_str(s))
            .map(|i| unsafe { isatty(i) == 1 }).unwrap_or(false)
}

#[deriving(Eq, PartialEq)]
enum PathCondition {
    BlockSpecial,
    CharacterSpecial,
    Directory,
    Exists,
    Regular,
    GroupIDFlag,
    SymLink,
    FIFO,
    Readable,
    Socket,
    NonEmpty,
    UserIDFlag,
    Writable,
    Executable,
}

#[cfg(not(windows))]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use libc::{stat, c_int, lstat, S_IFMT, S_IFLNK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFREG};
    use libc::{S_IFIFO, mode_t};
    static S_ISUID: mode_t = 0o4000;
    static S_ISGID: mode_t = 0o2000;
    static S_IFSOCK: c_int = 0o140000;

    enum Permission {
        Read    = 0o4,
        Write   = 0o2,
        Execute = 0o1,
    }
    let perm = |stat: stat, p: Permission| {
        use libc::{getgid, getuid};
        let (uid, gid) = unsafe { (getuid(), getgid()) };
        if uid == stat.st_uid {
            stat.st_mode & (p as mode_t << 6) != 0
        } else if gid == stat.st_gid {
            stat.st_mode & (p as mode_t << 3) != 0
        } else {
            stat.st_mode & (p as mode_t << 0) != 0
        }
    };

    let path = unsafe { path.to_c_str_unchecked() };
    let mut stat = unsafe { std::mem::zeroed() };
    if cond == SymLink {
        if unsafe { lstat(path.as_ptr(), &mut stat) } == 0 {
            if stat.st_mode as c_int & S_IFMT == S_IFLNK {
                return true;
            }
        }
        return false;
    }
    if unsafe { libc::stat(path.as_ptr(), &mut stat) } != 0 {
        return false;
    }
    let file_type = stat.st_mode as c_int & S_IFMT;
    match cond {
        BlockSpecial     => file_type == S_IFBLK,
        CharacterSpecial => file_type == S_IFCHR,
        Directory        => file_type == S_IFDIR,
        Exists           => true,
        Regular          => file_type == S_IFREG,
        GroupIDFlag      => stat.st_mode & S_ISGID != 0,
        SymLink          => true,
        FIFO             => file_type == S_IFIFO,
        Readable         => perm(stat, Read),
        Socket           => file_type == S_IFSOCK,
        NonEmpty         => stat.st_size > 0,
        UserIDFlag       => stat.st_mode & S_ISUID != 0,
        Writable         => perm(stat, Write),
        Executable       => perm(stat, Execute),
    }
}

#[cfg(windows)]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use std::io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe};
    use std::io::fs::{stat};
    use std::path::{Path};

    let path = match Path::new_opt(path) {
        Some(p) => p,
        None => return false,
    };
    let stat = match stat(&path) {
        Ok(s) => s,
        _ => return false,
    };
    match cond {
        BlockSpecial     => stat.kind == TypeBlockSpecial,
        CharacterSpecial => false,
        Directory        => stat.kind == TypeDirectory,
        Exists           => true,
        Regular          => stat.kind == TypeFile,
        GroupIDFlag      => false,
        SymLink          => false,
        FIFO             => stat.kind == TypeNamedPipe,
        Readable         => false, // TODO
        Socket           => false, // TODO?
        NonEmpty         => stat.size >  0,
        UserIDFlag       => false,
        Writable         => false, // TODO
        Executable       => false, // TODO
    }
}

<commit_msg>test: fix build<commit_after>#![crate_name = "uutest"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) mahkoh (ju.orth [at] gmail [dot] com)
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate libc;
extern crate num;

use std::os::{args_as_bytes};
use std::str::{from_utf8};
use num::bigint::{BigInt};

pub fn uumain(_: Vec<String>) -> int {
    let args = args_as_bytes();
    let args: Vec<&[u8]> = args.iter().map(|a| a.as_slice()).collect();
    let args = args.as_slice();
    if args.len() == 0 {
        return 2;
    }
    let args = match args[0] {
        b"[" => match args[args.len()-1] {
            b"]" => args.slice(1, args.len()-1),
            _ => return 2,
        },
        _ => args.slice(1, args.len()),
    };
    let rv = match args.len() {
        0 => false,
        1 => one(args),
        2 => two(args),
        3 => three(args),
        4 => four(args),
        _ => return 2,
    };
    1 - rv as int
}

fn one(args: &[&[u8]]) -> bool {
    args[0].len() > 0
}

fn two(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !one(args.slice_from(1)),
        b"-b" => path(args[1], BlockSpecial),
        b"-c" => path(args[1], CharacterSpecial),
        b"-d" => path(args[1], Directory),
        b"-e" => path(args[1], Exists),
        b"-f" => path(args[1], Regular),
        b"-g" => path(args[1], GroupIDFlag),
        b"-h" => path(args[1], SymLink),
        b"-L" => path(args[1], SymLink),
        b"-n" => one(args.slice_from(1)),
        b"-p" => path(args[1], FIFO),
        b"-r" => path(args[1], Readable),
        b"-S" => path(args[1], Socket),
        b"-s" => path(args[1], NonEmpty),
        b"-t" => isatty(args[1]),
        b"-u" => path(args[1], UserIDFlag),
        b"-w" => path(args[1], Writable),
        b"-x" => path(args[1], Executable),
        b"-z" => !one(args.slice_from(1)),
        _ => false,
    }
}

fn three(args: &[&[u8]]) -> bool {
    match args[1] {
        b"=" => args[0] == args[2],
        b"!=" => args[0] != args[2],
        b"-eq" => integers(args[0], args[2], Equal),
        b"-ne" => integers(args[0], args[2], Unequal),
        b"-gt" => integers(args[0], args[2], Greater),
        b"-ge" => integers(args[0], args[2], GreaterEqual),
        b"-lt" => integers(args[0], args[2], Less),
        b"-le" => integers(args[0], args[2], LessEqual),
        _ => match args[0] {
            b"!" => !two(args.slice_from(1)),
            _ => false,
        }
    }
}

fn four(args: &[&[u8]]) -> bool {
    match args[0] {
        b"!" => !three(args.slice_from(1)),
        _ => false,
    }
}

enum IntegerCondition {
    Equal,
    Unequal,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
}

fn integers(a: &[u8], b: &[u8], cond: IntegerCondition) -> bool {
    let (a, b): (&str, &str) = match (from_utf8(a), from_utf8(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    let (a, b): (BigInt, BigInt) = match (from_str(a), from_str(b)) {
        (Some(a), Some(b)) => (a, b),
        _ => return false,
    };
    match cond {
        Equal        => a == b,
        Unequal      => a != b,
        Greater      => a >  b,
        GreaterEqual => a >= b,
        Less         => a <  b,
        LessEqual    => a <= b,
    }
}

fn isatty(fd: &[u8]) -> bool {
    use libc::{isatty};
    from_utf8(fd).and_then(|s| from_str(s))
            .map(|i| unsafe { isatty(i) == 1 }).unwrap_or(false)
}

#[deriving(Eq, PartialEq)]
enum PathCondition {
    BlockSpecial,
    CharacterSpecial,
    Directory,
    Exists,
    Regular,
    GroupIDFlag,
    SymLink,
    FIFO,
    Readable,
    Socket,
    NonEmpty,
    UserIDFlag,
    Writable,
    Executable,
}

#[cfg(not(windows))]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use libc::{stat, lstat, S_IFMT, S_IFLNK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFREG};
    use libc::{S_IFIFO, mode_t};
    static S_ISUID: mode_t = 0o4000;
    static S_ISGID: mode_t = 0o2000;
    static S_IFSOCK: mode_t = 0o140000;

    enum Permission {
        Read    = 0o4,
        Write   = 0o2,
        Execute = 0o1,
    }
    let perm = |stat: stat, p: Permission| {
        use libc::{getgid, getuid};
        let (uid, gid) = unsafe { (getuid(), getgid()) };
        if uid == stat.st_uid {
            stat.st_mode & (p as mode_t << 6) != 0
        } else if gid == stat.st_gid {
            stat.st_mode & (p as mode_t << 3) != 0
        } else {
            stat.st_mode & (p as mode_t << 0) != 0
        }
    };

    let path = unsafe { path.to_c_str_unchecked() };
    let mut stat = unsafe { std::mem::zeroed() };
    if cond == SymLink {
        if unsafe { lstat(path.as_ptr(), &mut stat) } == 0 {
            if stat.st_mode & S_IFMT == S_IFLNK {
                return true;
            }
        }
        return false;
    }
    if unsafe { libc::stat(path.as_ptr(), &mut stat) } != 0 {
        return false;
    }
    let file_type = stat.st_mode & S_IFMT;
    match cond {
        BlockSpecial     => file_type == S_IFBLK,
        CharacterSpecial => file_type == S_IFCHR,
        Directory        => file_type == S_IFDIR,
        Exists           => true,
        Regular          => file_type == S_IFREG,
        GroupIDFlag      => stat.st_mode & S_ISGID != 0,
        SymLink          => true,
        FIFO             => file_type == S_IFIFO,
        Readable         => perm(stat, Read),
        Socket           => file_type == S_IFSOCK,
        NonEmpty         => stat.st_size > 0,
        UserIDFlag       => stat.st_mode & S_ISUID != 0,
        Writable         => perm(stat, Write),
        Executable       => perm(stat, Execute),
    }
}

#[cfg(windows)]
fn path(path: &[u8], cond: PathCondition) -> bool {
    use std::io::{TypeFile, TypeDirectory, TypeBlockSpecial, TypeNamedPipe};
    use std::io::fs::{stat};
    use std::path::{Path};

    let path = match Path::new_opt(path) {
        Some(p) => p,
        None => return false,
    };
    let stat = match stat(&path) {
        Ok(s) => s,
        _ => return false,
    };
    match cond {
        BlockSpecial     => stat.kind == TypeBlockSpecial,
        CharacterSpecial => false,
        Directory        => stat.kind == TypeDirectory,
        Exists           => true,
        Regular          => stat.kind == TypeFile,
        GroupIDFlag      => false,
        SymLink          => false,
        FIFO             => stat.kind == TypeNamedPipe,
        Readable         => false, // TODO
        Socket           => false, // TODO?
        NonEmpty         => stat.size >  0,
        UserIDFlag       => false,
        Writable         => false, // TODO
        Executable       => false, // TODO
    }
}

<|endoftext|>