<commit_before>use crate::utils::{
    indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, PatKind, QPath};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

declare_clippy_lint! {
    /// **What it does:**
    /// Finds patterns that reimplement `Option::ok_or`.
    ///
    /// **Why is this bad?**
    /// Concise code helps focusing on behavior instead of boilerplate.
    ///
    /// **Known problems:** None.
    ///
    /// **Examples:**
    /// ```rust
    /// let foo: Option<i32> = None;
    /// foo.map_or(Err("error"), |v| Ok(v));
    ///
    /// let foo: Option<i32> = None;
    /// foo.map_or(Err("error"), |v| Ok(v));
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// let foo: Option<i32> = None;
    /// foo.ok_or("error");
    /// ```
    pub MANUAL_OK_OR,
    pedantic,
    "finds patterns that can be encoded more concisely with `Option::ok_or`"
}

declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);

impl LateLintPass<'_> for ManualOkOr {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
        if in_external_macro(cx.sess(), scrutinee.span) {
            return;
        }

        if_chain! {
            if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind;
            if method_segment.ident.name == sym!(map_or);
            if args.len() == 3;
            let method_receiver = &args[0];
            let ty = cx.typeck_results().expr_ty(method_receiver);
            if is_type_diagnostic_item(cx, ty, sym::option_type);
            let or_expr = &args[1];
            if is_ok_wrapping(cx, &args[2]);
            if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
            if match_qpath(err_path, &paths::RESULT_ERR);
            if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
            if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
            if let Some(indent) = indent_of(cx, scrutinee.span);
            then {
                let reindented_err_arg_snippet =
                    reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
                span_lint_and_sugg(
                    cx,
                    MANUAL_OK_OR,
                    scrutinee.span,
                    "this pattern reimplements `Option::ok_or`",
                    "replace with",
                    format!(
                        "{}.ok_or({})",
                        method_receiver_snippet,
                        reindented_err_arg_snippet
                    ),
                    Applicability::MachineApplicable,
                );
            }
        }
    }
}

fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
    if let ExprKind::Path(ref qpath) = map_expr.kind {
        if match_qpath(qpath, &paths::RESULT_OK) {
            return true;
        }
    }
    if_chain! {
        if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind;
        let body = cx.tcx.hir().body(body_id);
        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
        if match_qpath(ok_path, &paths::RESULT_OK);
        if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind;
        if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res;
        then { param_id == ok_arg_path_id } else { false }
    }
}
<commit_msg>Remove duplication in the manual_ok_or lint example<commit_after>use crate::utils::{
    indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, PatKind, QPath};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

declare_clippy_lint! {
    /// **What it does:**
    /// Finds patterns that reimplement `Option::ok_or`.
    ///
    /// **Why is this bad?**
    /// Concise code helps focusing on behavior instead of boilerplate.
    ///
    /// **Known problems:** None.
    ///
    /// **Examples:**
    /// ```rust
    /// let foo: Option<i32> = None;
    /// foo.map_or(Err("error"), |v| Ok(v));
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// let foo: Option<i32> = None;
    /// foo.ok_or("error");
    /// ```
    pub MANUAL_OK_OR,
    pedantic,
    "finds patterns that can be encoded more concisely with `Option::ok_or`"
}

declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);

impl LateLintPass<'_> for ManualOkOr {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
        if in_external_macro(cx.sess(), scrutinee.span) {
            return;
        }

        if_chain! {
            if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind;
            if method_segment.ident.name == sym!(map_or);
            if args.len() == 3;
            let method_receiver = &args[0];
            let ty = cx.typeck_results().expr_ty(method_receiver);
            if is_type_diagnostic_item(cx, ty, sym::option_type);
            let or_expr = &args[1];
            if is_ok_wrapping(cx, &args[2]);
            if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
            if match_qpath(err_path, &paths::RESULT_ERR);
            if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
            if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
            if let Some(indent) = indent_of(cx, scrutinee.span);
            then {
                let reindented_err_arg_snippet =
                    reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
                span_lint_and_sugg(
                    cx,
                    MANUAL_OK_OR,
                    scrutinee.span,
                    "this pattern reimplements `Option::ok_or`",
                    "replace with",
                    format!(
                        "{}.ok_or({})",
                        method_receiver_snippet,
                        reindented_err_arg_snippet
                    ),
                    Applicability::MachineApplicable,
                );
            }
        }
    }
}

fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
    if let ExprKind::Path(ref qpath) = map_expr.kind {
        if match_qpath(qpath, &paths::RESULT_OK) {
            return true;
        }
    }
    if_chain! {
        if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind;
        let body = cx.tcx.hir().body(body_id);
        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
        if match_qpath(ok_path, &paths::RESULT_OK);
        if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind;
        if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res;
        then { param_id == ok_arg_path_id } else { false }
    }
}
<|endoftext|>


<commit_before>//! This module contains functions for retrieve the original AST from lowered `hir`.

#![deny(missing_docs_in_private_items)]

use rustc::hir;
use rustc::lint::LateContext;
use syntax::ast;
use utils::{is_expn_of, match_path, match_def_path, resolve_node, paths};

/// Convert a hir binary operator to the corresponding `ast` type.
pub fn binop(op: hir::BinOp_) -> ast::BinOpKind {
    match op {
        hir::BiEq => ast::BinOpKind::Eq,
        hir::BiGe => ast::BinOpKind::Ge,
        hir::BiGt => ast::BinOpKind::Gt,
        hir::BiLe => ast::BinOpKind::Le,
        hir::BiLt => ast::BinOpKind::Lt,
        hir::BiNe => ast::BinOpKind::Ne,
        hir::BiOr => ast::BinOpKind::Or,
        hir::BiAdd => ast::BinOpKind::Add,
        hir::BiAnd => ast::BinOpKind::And,
        hir::BiBitAnd => ast::BinOpKind::BitAnd,
        hir::BiBitOr => ast::BinOpKind::BitOr,
        hir::BiBitXor => ast::BinOpKind::BitXor,
        hir::BiDiv => ast::BinOpKind::Div,
        hir::BiMul => ast::BinOpKind::Mul,
        hir::BiRem => ast::BinOpKind::Rem,
        hir::BiShl => ast::BinOpKind::Shl,
        hir::BiShr => ast::BinOpKind::Shr,
        hir::BiSub => ast::BinOpKind::Sub,
    }
}

/// Represent a range akin to `ast::ExprKind::Range`.
#[derive(Debug, Copy, Clone)]
pub struct Range<'a> {
    /// The lower bound of the range, or `None` for ranges such as `..X`.
    pub start: Option<&'a hir::Expr>,
    /// The upper bound of the range, or `None` for ranges such as `X..`.
    pub end: Option<&'a hir::Expr>,
    /// Whether the interval is open or closed.
    pub limits: ast::RangeLimits,
}

/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
pub fn range(expr: &hir::Expr) -> Option<Range> {
    /// Find the field named `name` in the field. Always return `Some` for convenience.
    fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> {
        let expr = &fields.iter()
            .find(|field| field.name.node == name)
            .unwrap_or_else(|| panic!("missing {} field for range", name))
            .expr;

        Some(expr)
    }

    // The range syntax is expanded to literal paths starting with `core` or `std` depending on
    // `#[no_std]`. Testing both instead of resolving the paths.

    match expr.node {
        hir::ExprPath(ref path) => {
            if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
                Some(Range {
                    start: None,
                    end: None,
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else {
                None
            }
        },
        hir::ExprStruct(ref path, ref fields, None) => {
            if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
                Some(Range {
                    start: get_field("start", fields),
                    end: None,
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else if match_path(path, &paths::RANGE_INCLUSIVE_STD) ||
                      match_path(path, &paths::RANGE_INCLUSIVE) {
                Some(Range {
                    start: get_field("start", fields),
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::Closed,
                })
            } else if match_path(path, &paths::RANGE_STD) || match_path(path, &paths::RANGE) {
                Some(Range {
                    start: get_field("start", fields),
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else if match_path(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_path(path, &paths::RANGE_TO_INCLUSIVE) {
                Some(Range {
                    start: None,
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::Closed,
                })
            } else if match_path(path, &paths::RANGE_TO_STD) || match_path(path, &paths::RANGE_TO) {
                Some(Range {
                    start: None,
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else {
                None
            }
        },
        _ => None,
    }
}

/// Checks if a `let` decl is from a `for` loop desugaring.
pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
    if_let_chain! {[
        let hir::DeclLocal(ref loc) = decl.node,
        let Some(ref expr) = loc.init,
        let hir::ExprMatch(_, _, hir::MatchSource::ForLoopDesugar) = expr.node,
    ], {
        return true;
    }}
    false
}

/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
pub fn for_loop(expr: &hir::Expr) -> Option<(&hir::Pat, &hir::Expr, &hir::Expr)> {
    if_let_chain! {[
        let hir::ExprMatch(ref iterexpr, ref arms, _) = expr.node,
        let hir::ExprCall(_, ref iterargs) = iterexpr.node,
        iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
        let hir::ExprLoop(ref block, _, _) = arms[0].body.node,
        block.expr.is_none(),
        let [ ref let_stmt, ref body ] = *block.stmts,
        let hir::StmtDecl(ref decl, _) = let_stmt.node,
        let hir::DeclLocal(ref decl) = decl.node,
        let hir::StmtExpr(ref expr, _) = body.node,
    ], {
        return Some((&*decl.pat, &iterargs[0], expr));
    }}
    None
}

/// Represent the pre-expansion arguments of a `vec!` invocation.
pub enum VecArgs<'a> {
    /// `vec![elem; len]`
    Repeat(&'a hir::Expr, &'a hir::Expr),
    /// `vec![a, b, c]`
    Vec(&'a [hir::Expr]),
}

/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
pub fn vec_macro<'e>(cx: &LateContext, expr: &'e hir::Expr) -> Option<VecArgs<'e>> {
    if_let_chain!{[
        let hir::ExprCall(ref fun, ref args) = expr.node,
        let hir::ExprPath(ref path) = fun.node,
        is_expn_of(fun.span, "vec").is_some(),
    ], {
        let fun_def = resolve_node(cx, path, fun.id);
        return if match_def_path(cx.tcx, fun_def.def_id(), &paths::VEC_FROM_ELEM) && args.len() == 2 {
            // `vec![elem; size]` case
            Some(VecArgs::Repeat(&args[0], &args[1]))
        }
        else if match_def_path(cx.tcx, fun_def.def_id(), &paths::SLICE_INTO_VEC) && args.len() == 1 {
            // `vec![a, b, c]` case
            if_let_chain!{[
                let hir::ExprBox(ref boxed) = args[0].node,
                let hir::ExprArray(ref args) = boxed.node
            ], {
                return Some(VecArgs::Vec(&*args));
            }}

            None
        }
        else {
            None
        };
    }}

    None
}
<commit_msg>Check for `for` loop desugaring in highering<commit_after>//! This module contains functions for retrieve the original AST from lowered `hir`.

#![deny(missing_docs_in_private_items)]

use rustc::hir;
use rustc::lint::LateContext;
use syntax::ast;
use utils::{is_expn_of, match_path, match_def_path, resolve_node, paths};

/// Convert a hir binary operator to the corresponding `ast` type.
pub fn binop(op: hir::BinOp_) -> ast::BinOpKind {
    match op {
        hir::BiEq => ast::BinOpKind::Eq,
        hir::BiGe => ast::BinOpKind::Ge,
        hir::BiGt => ast::BinOpKind::Gt,
        hir::BiLe => ast::BinOpKind::Le,
        hir::BiLt => ast::BinOpKind::Lt,
        hir::BiNe => ast::BinOpKind::Ne,
        hir::BiOr => ast::BinOpKind::Or,
        hir::BiAdd => ast::BinOpKind::Add,
        hir::BiAnd => ast::BinOpKind::And,
        hir::BiBitAnd => ast::BinOpKind::BitAnd,
        hir::BiBitOr => ast::BinOpKind::BitOr,
        hir::BiBitXor => ast::BinOpKind::BitXor,
        hir::BiDiv => ast::BinOpKind::Div,
        hir::BiMul => ast::BinOpKind::Mul,
        hir::BiRem => ast::BinOpKind::Rem,
        hir::BiShl => ast::BinOpKind::Shl,
        hir::BiShr => ast::BinOpKind::Shr,
        hir::BiSub => ast::BinOpKind::Sub,
    }
}

/// Represent a range akin to `ast::ExprKind::Range`.
#[derive(Debug, Copy, Clone)]
pub struct Range<'a> {
    /// The lower bound of the range, or `None` for ranges such as `..X`.
    pub start: Option<&'a hir::Expr>,
    /// The upper bound of the range, or `None` for ranges such as `X..`.
    pub end: Option<&'a hir::Expr>,
    /// Whether the interval is open or closed.
    pub limits: ast::RangeLimits,
}

/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
pub fn range(expr: &hir::Expr) -> Option<Range> {
    /// Find the field named `name` in the field. Always return `Some` for convenience.
    fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> {
        let expr = &fields.iter()
            .find(|field| field.name.node == name)
            .unwrap_or_else(|| panic!("missing {} field for range", name))
            .expr;

        Some(expr)
    }

    // The range syntax is expanded to literal paths starting with `core` or `std` depending on
    // `#[no_std]`. Testing both instead of resolving the paths.

    match expr.node {
        hir::ExprPath(ref path) => {
            if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
                Some(Range {
                    start: None,
                    end: None,
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else {
                None
            }
        },
        hir::ExprStruct(ref path, ref fields, None) => {
            if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
                Some(Range {
                    start: get_field("start", fields),
                    end: None,
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else if match_path(path, &paths::RANGE_INCLUSIVE_STD) ||
                      match_path(path, &paths::RANGE_INCLUSIVE) {
                Some(Range {
                    start: get_field("start", fields),
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::Closed,
                })
            } else if match_path(path, &paths::RANGE_STD) || match_path(path, &paths::RANGE) {
                Some(Range {
                    start: get_field("start", fields),
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else if match_path(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_path(path, &paths::RANGE_TO_INCLUSIVE) {
                Some(Range {
                    start: None,
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::Closed,
                })
            } else if match_path(path, &paths::RANGE_TO_STD) || match_path(path, &paths::RANGE_TO) {
                Some(Range {
                    start: None,
                    end: get_field("end", fields),
                    limits: ast::RangeLimits::HalfOpen,
                })
            } else {
                None
            }
        },
        _ => None,
    }
}

/// Checks if a `let` decl is from a `for` loop desugaring.
pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
    if_let_chain! {[
        let hir::DeclLocal(ref loc) = decl.node,
        let Some(ref expr) = loc.init,
        let hir::ExprMatch(_, _, hir::MatchSource::ForLoopDesugar) = expr.node,
    ], {
        return true;
    }}
    false
}

/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
pub fn for_loop(expr: &hir::Expr) -> Option<(&hir::Pat, &hir::Expr, &hir::Expr)> {
    if_let_chain! {[
        let hir::ExprMatch(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.node,
        let hir::ExprCall(_, ref iterargs) = iterexpr.node,
        iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
        let hir::ExprLoop(ref block, _, _) = arms[0].body.node,
        block.expr.is_none(),
        let [ ref let_stmt, ref body ] = *block.stmts,
        let hir::StmtDecl(ref decl, _) = let_stmt.node,
        let hir::DeclLocal(ref decl) = decl.node,
        let hir::StmtExpr(ref expr, _) = body.node,
    ], {
        return Some((&*decl.pat, &iterargs[0], expr));
    }}
    None
}

/// Represent the pre-expansion arguments of a `vec!` invocation.
pub enum VecArgs<'a> {
    /// `vec![elem; len]`
    Repeat(&'a hir::Expr, &'a hir::Expr),
    /// `vec![a, b, c]`
    Vec(&'a [hir::Expr]),
}

/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
pub fn vec_macro<'e>(cx: &LateContext, expr: &'e hir::Expr) -> Option<VecArgs<'e>> {
    if_let_chain!{[
        let hir::ExprCall(ref fun, ref args) = expr.node,
        let hir::ExprPath(ref path) = fun.node,
        is_expn_of(fun.span, "vec").is_some(),
    ], {
        let fun_def = resolve_node(cx, path, fun.id);
        return if match_def_path(cx.tcx, fun_def.def_id(), &paths::VEC_FROM_ELEM) && args.len() == 2 {
            // `vec![elem; size]` case
            Some(VecArgs::Repeat(&args[0], &args[1]))
        }
        else if match_def_path(cx.tcx, fun_def.def_id(), &paths::SLICE_INTO_VEC) && args.len() == 1 {
            // `vec![a, b, c]` case
            if_let_chain!{[
                let hir::ExprBox(ref boxed) = args[0].node,
                let hir::ExprArray(ref args) = boxed.node
            ], {
                return Some(VecArgs::Vec(&*args));
            }}

            None
        }
        else {
            None
        };
    }}

    None
}
<|endoftext|>


<commit_before>// xfail-fast
// (Not sure why, though -- FIXME (tjc)
import to_str::*;
import to_str::to_str;

class cat {
  priv {
    let mut meows : uint;
    fn meow() {
      #error("Meow");
      self.meows += 1u;
      if self.meows % 5u == 0u {
          self.how_hungry += 1;
      }
    }
  }

  let mut how_hungry : int;
  let name : str;

  new(in_x : uint, in_y : int, in_name: str)
    { self.meows = in_x; self.how_hungry = in_y; self.name = in_name; }

  fn speak() { self.meow(); }

  fn eat() -> bool {
    if self.how_hungry > 0 {
        #error("OM NOM NOM");
        self.how_hungry -= 2;
        ret true;
    }
    else {
        #error("Not hungry!");
        ret false;
    }
  }
}

impl of to_str for cat {
  fn to_str() -> str { self.name }
}

fn print_out<T: to_str>(thing: T, expected: str) {
  let actual = thing.to_str();
  #debug("%s", actual);
  assert(actual == expected);
}

fn main() {
  let nyan : to_str  = cat(0u, 2, "nyan") as to_str;
  print_out(nyan, "nyan");
}
<commit_msg>Remove an xfail-fast, as this seems to work on Windows now<commit_after>import to_str::*;
import to_str::to_str;

class cat {
  priv {
    let mut meows : uint;
    fn meow() {
      #error("Meow");
      self.meows += 1u;
      if self.meows % 5u == 0u {
          self.how_hungry += 1;
      }
    }
  }

  let mut how_hungry : int;
  let name : str;

  new(in_x : uint, in_y : int, in_name: str)
    { self.meows = in_x; self.how_hungry = in_y; self.name = in_name; }

  fn speak() { self.meow(); }

  fn eat() -> bool {
    if self.how_hungry > 0 {
        #error("OM NOM NOM");
        self.how_hungry -= 2;
        ret true;
    }
    else {
        #error("Not hungry!");
        ret false;
    }
  }
}

impl of to_str for cat {
  fn to_str() -> str { self.name }
}

fn print_out<T: to_str>(thing: T, expected: str) {
  let actual = thing.to_str();
  #debug("%s", actual);
  assert(actual == expected);
}

fn main() {
  let nyan : to_str  = cat(0u, 2, "nyan") as to_str;
  print_out(nyan, "nyan");
}
<|endoftext|>


<commit_before>use std::io::{Read, Seek};
use byteorder::{LittleEndian as E, ReadBytesExt};
use gfx;
use gfx::format::I8Norm;
use render::{ObjectVertex, NUM_COLOR_IDS, COLOR_ID_BODY};

const MAX_SLOTS: u32 = 3;

#[derive(Clone)]
pub struct Mesh<R: gfx::Resources> {
    pub slice: gfx::Slice<R>,
    pub buffer: gfx::handle::Buffer<R, ObjectVertex>,
    pub offset: [f32; 3],
}

#[derive(Clone)]
pub struct Polygon {
    pub middle: [f32; 3],
    pub normal: [f32; 3],
    pub sample_range: (u16, u16),
}

#[derive(Clone)]
pub struct Shape {
    pub polygons: Vec<Polygon>,
    pub samples: Vec<[f32; 3]>,
}

#[derive(Clone)]
pub struct Wheel<R: gfx::Resources> {
    pub mesh: Option<Mesh<R>>,
    pub steer: u32,
    pub pos: [f32; 3],
    pub width: u32,
    pub radius: u32,
}

#[derive(Clone)]
pub struct Debrie<R: gfx::Resources> {
    pub mesh: Mesh<R>,
    pub shape: Shape,
}

#[derive(Clone)]
pub struct Slot<R: gfx::Resources> {
    pub mesh: Option<Mesh<R>>,
    pub scale: f32,
    pub pos: [f32; 3],
    pub angle: i32,
}

#[derive(Clone)]
pub struct Model<R: gfx::Resources> {
    pub body: Mesh<R>,
    pub shape: Shape,
    pub color: [u32; 2],
    pub wheels: Vec<Wheel<R>>,
    pub debris: Vec<Debrie<R>>,
    pub slots: Vec<Slot<R>>,
}

fn read_vec<I: ReadBytesExt>(source: &mut I) -> [i32; 3] {
    [
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
    ]
}

pub fn load_c3d<I, R, F>(source: &mut I, factory: &mut F) -> Mesh<R> where
    I: ReadBytesExt,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    let version = source.read_u32::<E>().unwrap();
    assert_eq!(version, 8);
    let num_positions = source.read_u32::<E>().unwrap();
    let num_normals   = source.read_u32::<E>().unwrap();
    let num_polygons  = source.read_u32::<E>().unwrap();
    let _total_verts  = source.read_u32::<E>().unwrap();

    let coord_max = read_vec(source);
    let coord_min = read_vec(source);
    let parent_off = read_vec(source);
    debug!("\tBound {:?} to {:?} with offset {:?}", coord_min, coord_max, parent_off);
    let _max_radius = source.read_u32::<E>().unwrap();
    let _parent_rot = read_vec(source);
    for _ in 0 .. (1+3+9) {
        source.read_f64::<E>().unwrap();
    }

    debug!("\tReading {} positions...", num_positions);
    let mut positions = Vec::with_capacity(num_positions as usize);
    for _ in 0 .. num_positions {
        read_vec(source); //unknown
        let pos = [
            source.read_i8().unwrap(),
            source.read_i8().unwrap(),
            source.read_i8().unwrap(),
            1];
        let _sort_info = source.read_u32::<E>().unwrap();
        positions.push(pos);
    }

    debug!("\tReading {} normals...", num_normals);
    let mut normals = Vec::with_capacity(num_normals as usize);
    for _ in 0 .. num_normals {
        let mut norm = [0u8; 4];
        source.read_exact(&mut norm).unwrap();
        let _sort_info = source.read_u32::<E>().unwrap();
        normals.push(norm);
    }

    debug!("\tReading {} polygons...", num_polygons);
    let mut vertices = Vec::with_capacity(num_polygons as usize * 3);
    for i in 0 .. num_polygons {
        let num_corners = source.read_u32::<E>().unwrap();
        assert_eq!(num_corners, 3);
        let _sort_info = source.read_u32::<E>().unwrap();
        let color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
        let mut dummy = [0; 4];
        source.read_exact(&mut dummy[..4]).unwrap(); //skip flat normal
        source.read_exact(&mut dummy[..3]).unwrap(); //skip middle point
        for k in 0..num_corners {
            let pid = source.read_u32::<E>().unwrap();
            let nid = source.read_u32::<E>().unwrap();
            let v = (i*3+k, (positions[pid as usize], normals[nid as usize], color));
            vertices.push(v);
        }
    }

    // sorted variable polygons
    for _ in 0 .. 3 {
        for _ in 0 .. num_polygons {
            let _poly_ind = source.read_u32::<E>().unwrap();
        }
    }

    let convert = |(p, n, c): ([i8; 4], [u8; 4], [u32; 2])| ObjectVertex {
        pos: p,
        color: if c[0] < NUM_COLOR_IDS { c[0] } else { COLOR_ID_BODY },
        normal: [I8Norm(n[0] as i8), I8Norm(n[1] as i8), I8Norm(n[2] as i8), I8Norm(n[3] as i8)],
    };
    let do_compact = true;

    let mut gpu_verts = Vec::new();
    let (vbuf, slice) = if do_compact {
        debug!("\tCompacting...");
        vertices.sort_by_key(|v| v.1);
        //vertices.dedup();
        let mut indices = vec![0; vertices.len()];
        let mut last = vertices[0].1;
        last.2[0] ^= 1; //change something
        let mut v_id = 0;
        for v in vertices.into_iter() {
            if v.1 != last {
                last = v.1;
                v_id = gpu_verts.len() as u16;
                gpu_verts.push(convert(v.1));
            }
            indices[v.0 as usize] = v_id;
        }
        factory.create_vertex_buffer_with_slice(&gpu_verts, &indices[..])
    }else {
        for v in vertices.into_iter() {
            gpu_verts.push(convert(v.1));
        }
        factory.create_vertex_buffer_with_slice(&gpu_verts, ())
    };

    debug!("\tGot {} GPU vertices...", gpu_verts.len());
    Mesh {
        slice: slice,
        buffer: vbuf,
        offset: [parent_off[0] as f32, parent_off[1] as f32, parent_off[2] as f32],
    }
}

pub fn load_c3d_shape<I>(source: &mut I) -> Shape where
    I: ReadBytesExt + Seek,
{
    use std::io::SeekFrom::Current;

    let version = source.read_u32::<E>().unwrap();
    assert_eq!(version, 8);
    let num_positions = source.read_u32::<E>().unwrap();
    let num_normals   = source.read_u32::<E>().unwrap();
    let num_polygons  = source.read_u32::<E>().unwrap();
    let _total_verts  = source.read_u32::<E>().unwrap();

    let mut shape = Shape {
        polygons: Vec::with_capacity(num_polygons as usize),
        samples: Vec::new(),
    };
    let coord_max = read_vec(source);
    let coord_min = read_vec(source);
    debug!("\tBound {:?} to {:?}", coord_min, coord_max);

    source.seek(Current(
        (3+1+3) * 4 + // parent offset, max radius, and parent rotation
        (1+3+9) * 8 + // ?
        (num_positions as i64) * (3*4 + 3*1 + 4) + // positions
        (num_normals as i64) * (4*1 + 4) + // normals
        0)).unwrap();

    debug!("\tReading {} polygons...", num_polygons);
    for _ in 0 .. num_polygons {
        let num_corners = source.read_u32::<E>().unwrap();
        assert!(3 <= num_corners && num_corners <= 4);
        source.seek(Current(4 + 4 + 4)).unwrap(); // sort info and color
        let mut d = [0i8; 7];
        for b in d.iter_mut() {
            *b = source.read_i8().unwrap();
        }
        source.seek(Current((num_corners as i64) * (4 + 4))).unwrap(); // vertices
        //TODO: more samples!
        let mid = [d[4] as f32, d[5] as f32, d[6] as f32];
        shape.polygons.push(Polygon {
            middle: mid,
            normal: [d[0] as f32 / 128.0, d[1] as f32 / 128.0, d[2] as f32 / 128.0],
            sample_range: (shape.samples.len() as u16, shape.samples.len() as u16 + 1),
        });
        shape.samples.push(mid);
    }

    source.seek(Current(3 * (num_polygons as i64) * 4)).unwrap(); // sorted var polys

    shape
}

pub fn load_m3d<I, R, F>(source: &mut I, factory: &mut F) -> Model<R> where
    I: ReadBytesExt + Seek,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    debug!("\tReading the body...");
    let mut model = Model {
        body: load_c3d(source, factory),
        shape: Shape {
            polygons: Vec::new(),
            samples: Vec::new(),
        },
        color: [0, 0],
        wheels: Vec::new(),
        debris: Vec::new(),
        slots: Vec::new(),
    };
    let _bounds = read_vec(source);
    let _max_radius = source.read_u32::<E>().unwrap();
    let num_wheels = source.read_u32::<E>().unwrap();
    let num_debris = source.read_u32::<E>().unwrap();
    model.color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
    model.wheels.reserve_exact(num_wheels as usize);
    model.debris.reserve_exact(num_debris as usize);

    debug!("\tReading {} wheels...", num_wheels);
    for _ in 0 .. num_wheels {
        let steer = source.read_u32::<E>().unwrap();
        let pos = [
            source.read_f64::<E>().unwrap() as f32,
            source.read_f64::<E>().unwrap() as f32,
            source.read_f64::<E>().unwrap() as f32,
        ];
        let width = source.read_u32::<E>().unwrap();
        let radius = source.read_u32::<E>().unwrap();
        let _bound_index = source.read_u32::<E>().unwrap();
        debug!("\tSteer {}, width {}, radius {}", steer, width, radius);
        model.wheels.push(Wheel {
            mesh: if steer != 0 {
                Some(load_c3d(source, factory))
            } else {None},
            steer: steer,
            pos: pos,
            width: width,
            radius: radius,
        })
    }

    debug!("\tReading {} debris...", num_debris);
    for _ in 0 .. num_debris {
        model.debris.push(Debrie {
            mesh: load_c3d(source, factory),
            shape: load_c3d_shape(source),
        })
    }

    debug!("\tReading the physical shape...");
    model.shape = load_c3d_shape(source);

    let slot_mask = source.read_u32::<E>().unwrap();
    debug!("\tReading {} slot mask...", slot_mask);
    if slot_mask != 0 {
        for i in 0 .. MAX_SLOTS {
            let pos = read_vec(source);
            let angle = source.read_i32::<E>().unwrap();
            if slot_mask & (1<<i) != 0 {
                debug!("\tSlot {} at pos {:?} and angle of {}", i, pos, angle);
                model.slots.push(Slot {
                    mesh: None,
                    scale: 1.0,
                    pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32],
                    angle: angle,
                });
            }
        }
    }

    model
}
<commit_msg>Reading physics params<commit_after>use std::io::{Read, Seek};
use byteorder::{LittleEndian as E, ReadBytesExt};
use gfx;
use gfx::format::I8Norm;
use render::{ObjectVertex, NUM_COLOR_IDS, COLOR_ID_BODY};

const MAX_SLOTS: u32 = 3;

#[derive(Clone)]
pub struct Physics {
    pub mass: f32,
    pub rcm: [f32; 3],
    pub jacobi: [[f32; 3]; 3],
}

#[derive(Clone)]
pub struct Mesh<R: gfx::Resources> {
    pub slice: gfx::Slice<R>,
    pub buffer: gfx::handle::Buffer<R, ObjectVertex>,
    pub offset: [f32; 3],
    pub physics: Physics,
}

#[derive(Clone)]
pub struct Polygon {
    pub middle: [f32; 3],
    pub normal: [f32; 3],
    pub sample_range: (u16, u16),
}

#[derive(Clone)]
pub struct Shape {
    pub polygons: Vec<Polygon>,
    pub samples: Vec<[f32; 3]>,
}

#[derive(Clone)]
pub struct Wheel<R: gfx::Resources> {
    pub mesh: Option<Mesh<R>>,
    pub steer: u32,
    pub pos: [f32; 3],
    pub width: u32,
    pub radius: u32,
}

#[derive(Clone)]
pub struct Debrie<R: gfx::Resources> {
    pub mesh: Mesh<R>,
    pub shape: Shape,
}

#[derive(Clone)]
pub struct Slot<R: gfx::Resources> {
    pub mesh: Option<Mesh<R>>,
    pub scale: f32,
    pub pos: [f32; 3],
    pub angle: i32,
}

#[derive(Clone)]
pub struct Model<R: gfx::Resources> {
    pub body: Mesh<R>,
    pub shape: Shape,
    pub color: [u32; 2],
    pub wheels: Vec<Wheel<R>>,
    pub debris: Vec<Debrie<R>>,
    pub slots: Vec<Slot<R>>,
}

fn read_vec<I: ReadBytesExt>(source: &mut I) -> [i32; 3] {
    [
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
        source.read_i32::<E>().unwrap(),
    ]
}

pub fn load_c3d<I, R, F>(source: &mut I, factory: &mut F) -> Mesh<R> where
    I: ReadBytesExt,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    let version = source.read_u32::<E>().unwrap();
    assert_eq!(version, 8);
    let num_positions = source.read_u32::<E>().unwrap();
    let num_normals   = source.read_u32::<E>().unwrap();
    let num_polygons  = source.read_u32::<E>().unwrap();
    let _total_verts  = source.read_u32::<E>().unwrap();

    let coord_max = read_vec(source);
    let coord_min = read_vec(source);
    let parent_off = read_vec(source);
    debug!("\tBound {:?} to {:?} with offset {:?}", coord_min, coord_max, parent_off);
    let _max_radius = source.read_u32::<E>().unwrap();
    let _parent_rot = read_vec(source);
    let physics = {
        let mut q = [0.0f32; 1+3+9];
        for qel in q.iter_mut() {
            *qel = source.read_f64::<E>().unwrap() as f32;
        }
        Physics {
            mass: q[0],
            rcm: [q[1], q[2], q[3]],
            jacobi: [
                [q[4], q[5], q[6]],
                [q[7], q[8], q[9]],
                [q[10], q[11], q[12]],
            ],
        }
    };

    debug!("\tReading {} positions...", num_positions);
    let mut positions = Vec::with_capacity(num_positions as usize);
    for _ in 0 .. num_positions {
        read_vec(source); //unknown
        let pos = [
            source.read_i8().unwrap(),
            source.read_i8().unwrap(),
            source.read_i8().unwrap(),
            1];
        let _sort_info = source.read_u32::<E>().unwrap();
        positions.push(pos);
    }

    debug!("\tReading {} normals...", num_normals);
    let mut normals = Vec::with_capacity(num_normals as usize);
    for _ in 0 .. num_normals {
        let mut norm = [0u8; 4];
        source.read_exact(&mut norm).unwrap();
        let _sort_info = source.read_u32::<E>().unwrap();
        normals.push(norm);
    }

    debug!("\tReading {} polygons...", num_polygons);
    let mut vertices = Vec::with_capacity(num_polygons as usize * 3);
    for i in 0 .. num_polygons {
        let num_corners = source.read_u32::<E>().unwrap();
        assert_eq!(num_corners, 3);
        let _sort_info = source.read_u32::<E>().unwrap();
        let color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
        let mut dummy = [0; 4];
        source.read_exact(&mut dummy[..4]).unwrap(); //skip flat normal
        source.read_exact(&mut dummy[..3]).unwrap(); //skip middle point
        for k in 0..num_corners {
            let pid = source.read_u32::<E>().unwrap();
            let nid = source.read_u32::<E>().unwrap();
            let v = (i*3+k, (positions[pid as usize], normals[nid as usize], color));
            vertices.push(v);
        }
    }

    // sorted variable polygons
    for _ in 0 .. 3 {
        for _ in 0 .. num_polygons {
            let _poly_ind = source.read_u32::<E>().unwrap();
        }
    }

    let convert = |(p, n, c): ([i8; 4], [u8; 4], [u32; 2])| ObjectVertex {
        pos: p,
        color: if c[0] < NUM_COLOR_IDS { c[0] } else { COLOR_ID_BODY },
        normal: [I8Norm(n[0] as i8), I8Norm(n[1] as i8), I8Norm(n[2] as i8), I8Norm(n[3] as i8)],
    };
    let do_compact = true;

    let mut gpu_verts = Vec::new();
    let (vbuf, slice) = if do_compact {
        debug!("\tCompacting...");
        vertices.sort_by_key(|v| v.1);
        //vertices.dedup();
        let mut indices = vec![0; vertices.len()];
        let mut last = vertices[0].1;
        last.2[0] ^= 1; //change something
        let mut v_id = 0;
        for v in vertices.into_iter() {
            if v.1 != last {
                last = v.1;
                v_id = gpu_verts.len() as u16;
                gpu_verts.push(convert(v.1));
            }
            indices[v.0 as usize] = v_id;
        }
        factory.create_vertex_buffer_with_slice(&gpu_verts, &indices[..])
    }else {
        for v in vertices.into_iter() {
            gpu_verts.push(convert(v.1));
        }
        factory.create_vertex_buffer_with_slice(&gpu_verts, ())
    };

    debug!("\tGot {} GPU vertices...", gpu_verts.len());
    Mesh {
        slice: slice,
        buffer: vbuf,
        offset: [parent_off[0] as f32, parent_off[1] as f32, parent_off[2] as f32],
        physics: physics,
    }
}

pub fn load_c3d_shape<I>(source: &mut I) -> Shape where
    I: ReadBytesExt + Seek,
{
    use std::io::SeekFrom::Current;

    let version = source.read_u32::<E>().unwrap();
    assert_eq!(version, 8);
    let num_positions = source.read_u32::<E>().unwrap();
    let num_normals   = source.read_u32::<E>().unwrap();
    let num_polygons  = source.read_u32::<E>().unwrap();
    let _total_verts  = source.read_u32::<E>().unwrap();

    let mut shape = Shape {
        polygons: Vec::with_capacity(num_polygons as usize),
        samples: Vec::new(),
    };
    let coord_max = read_vec(source);
    let coord_min = read_vec(source);
    debug!("\tBound {:?} to {:?}", coord_min, coord_max);

    source.seek(Current(
        (3+1+3) * 4 + // parent offset, max radius, and parent rotation
        (1+3+9) * 8 + // ?
        (num_positions as i64) * (3*4 + 3*1 + 4) + // positions
        (num_normals as i64) * (4*1 + 4) + // normals
        0)).unwrap();

    debug!("\tReading {} polygons...", num_polygons);
    for _ in 0 .. num_polygons {
        let num_corners = source.read_u32::<E>().unwrap();
        assert!(3 <= num_corners && num_corners <= 4);
        source.seek(Current(4 + 4 + 4)).unwrap(); // sort info and color
        let mut d = [0i8; 7];
        for b in d.iter_mut() {
            *b = source.read_i8().unwrap();
        }
        source.seek(Current((num_corners as i64) * (4 + 4))).unwrap(); // vertices
        //TODO: more samples!
        let mid = [d[4] as f32, d[5] as f32, d[6] as f32];
        shape.polygons.push(Polygon {
            middle: mid,
            normal: [d[0] as f32 / 128.0, d[1] as f32 / 128.0, d[2] as f32 / 128.0],
            sample_range: (shape.samples.len() as u16, shape.samples.len() as u16 + 1),
        });
        shape.samples.push(mid);
    }

    source.seek(Current(3 * (num_polygons as i64) * 4)).unwrap(); // sorted var polys

    shape
}

pub fn load_m3d<I, R, F>(source: &mut I, factory: &mut F) -> Model<R> where
    I: ReadBytesExt + Seek,
    R: gfx::Resources,
    F: gfx::traits::FactoryExt<R>,
{
    debug!("\tReading the body...");
    let mut model = Model {
        body: load_c3d(source, factory),
        shape: Shape {
            polygons: Vec::new(),
            samples: Vec::new(),
        },
        color: [0, 0],
        wheels: Vec::new(),
        debris: Vec::new(),
        slots: Vec::new(),
    };
    let _bounds = read_vec(source);
    let _max_radius = source.read_u32::<E>().unwrap();
    let num_wheels = source.read_u32::<E>().unwrap();
    let num_debris = source.read_u32::<E>().unwrap();
    model.color = [source.read_u32::<E>().unwrap(), source.read_u32::<E>().unwrap()];
    model.wheels.reserve_exact(num_wheels as usize);
    model.debris.reserve_exact(num_debris as usize);

    debug!("\tReading {} wheels...", num_wheels);
    for _ in 0 .. num_wheels {
        let steer = source.read_u32::<E>().unwrap();
        let pos = [
            source.read_f64::<E>().unwrap() as f32,
            source.read_f64::<E>().unwrap() as f32,
            source.read_f64::<E>().unwrap() as f32,
        ];
        let width = source.read_u32::<E>().unwrap();
        let radius = source.read_u32::<E>().unwrap();
        let _bound_index = source.read_u32::<E>().unwrap();
        debug!("\tSteer {}, width {}, radius {}", steer, width, radius);
        model.wheels.push(Wheel {
            mesh: if steer != 0 {
                Some(load_c3d(source, factory))
            } else {None},
            steer: steer,
            pos: pos,
            width: width,
            radius: radius,
        })
    }

    debug!("\tReading {} debris...", num_debris);
    for _ in 0 .. num_debris {
        model.debris.push(Debrie {
            mesh: load_c3d(source, factory),
            shape: load_c3d_shape(source),
        })
    }

    debug!("\tReading the physical shape...");
    model.shape = load_c3d_shape(source);

    let slot_mask = source.read_u32::<E>().unwrap();
    debug!("\tReading {} slot mask...", slot_mask);
    if slot_mask != 0 {
        for i in 0 .. MAX_SLOTS {
            let pos = read_vec(source);
            let angle = source.read_i32::<E>().unwrap();
            if slot_mask & (1<<i) != 0 {
                debug!("\tSlot {} at pos {:?} and angle of {}", i, pos, angle);
                model.slots.push(Slot {
                    mesh: None,
                    scale: 1.0,
                    pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32],
                    angle: angle,
                });
            }
        }
    }

    model
}
<|endoftext|>


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

//! APIs for memory profiling.

#![deny(missing_docs)]

use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use std::marker::Send;
use std::sync::mpsc::Sender;

/// A trait to abstract away the various kinds of message senders we use.
pub trait OpaqueSender<T> {
    /// Send a message.
    fn send(&self, message: T);
}

impl<T> OpaqueSender<T> for Sender<T> {
    fn send(&self, message: T) {
        Sender::send(self, message).unwrap();
    }
}

/// Front-end representation of the profiler used to communicate with the
/// profiler.
#[derive(Clone, Deserialize, Serialize)]
pub struct ProfilerChan(pub IpcSender<ProfilerMsg>);

impl ProfilerChan {
    /// Send `msg` on this `IpcSender`.
    ///
    /// Panics if the send fails.
    pub fn send(&self, msg: ProfilerMsg) {
        self.0.send(msg).unwrap();
    }

    /// Runs `f()` with memory profiling.
    pub fn run_with_memory_reporting<F, M, T, C>(&self, f: F,
                                                 reporter_name: String,
                                                 channel_for_reporter: C,
                                                 msg: M)
        where F: FnOnce(),
              M: Fn(ReportsChan) -> T + Send + 'static,
              T: Send + 'static,
              C: OpaqueSender<T> + Send + 'static
    {
        // Register the memory reporter.
        let (reporter_sender, reporter_receiver) = ipc::channel().unwrap();
        ROUTER.add_route(reporter_receiver.to_opaque(), box move |message| {
            // Just injects an appropriate event into the paint thread's queue.
            let request: ReporterRequest = message.to().unwrap();
            channel_for_reporter.send(msg(request.reports_channel));
        });
        self.send(ProfilerMsg::RegisterReporter(reporter_name.clone(),
                                                Reporter(reporter_sender)));

        f();

        self.send(ProfilerMsg::UnregisterReporter(reporter_name));
    }
}

/// The various kinds of memory measurement.
///
/// Here "explicit" means explicit memory allocations done by the application. It includes
/// allocations made at the OS level (via functions such as VirtualAlloc, vm_allocate, and mmap),
/// allocations made at the heap allocation level (via functions such as malloc, calloc, realloc,
/// memalign, operator new, and operator new[]) and where possible, the overhead of the heap
/// allocator itself. It excludes memory that is mapped implicitly such as code and data segments,
/// and thread stacks. "explicit" is not guaranteed to cover every explicit allocation, but it does
/// cover most (including the entire heap), and therefore it is the single best number to focus on
/// when trying to reduce memory usage.
#[derive(Deserialize, Serialize)]
pub enum ReportKind {
    /// A size measurement for an explicit allocation on the jemalloc heap. This should be used
    /// for any measurements done via the `HeapSizeOf` trait.
    ExplicitJemallocHeapSize,

    /// A size measurement for an explicit allocation on the system heap. Only likely to be used
    /// for external C or C++ libraries that don't use jemalloc.
    ExplicitSystemHeapSize,

    /// A size measurement for an explicit allocation not on the heap, e.g. via mmap().
    ExplicitNonHeapSize,

    /// A size measurement for an explicit allocation whose location is unknown or uncertain.
    ExplicitUnknownLocationSize,

    /// A size measurement for a non-explicit allocation. This kind is used for global
    /// measurements such as "resident" and "vsize", and also for measurements that cross-cut the
    /// measurements grouped under "explicit", e.g. by grouping those measurements in a way that's
    /// different to how they are grouped under "explicit".
    NonExplicitSize,
}

/// A single memory-related measurement.
#[derive(Deserialize, Serialize)]
pub struct Report {
    /// The identifying path for this report.
    pub path: Vec<String>,

    /// The report kind.
    pub kind: ReportKind,

    /// The size, in bytes.
    pub size: usize,
}

/// A channel through which memory reports can be sent.
#[derive(Clone, Deserialize, Serialize)]
pub struct ReportsChan(pub IpcSender<Vec<Report>>);

impl ReportsChan {
    /// Send `report` on this `IpcSender`.
    ///
    /// Panics if the send fails.
    pub fn send(&self, report: Vec<Report>) {
        self.0.send(report).unwrap();
    }
}

/// The protocol used to send reporter requests.
#[derive(Deserialize, Serialize)]
pub struct ReporterRequest {
    /// The channel on which reports are to be sent.
    pub reports_channel: ReportsChan,
}

/// A memory reporter is capable of measuring some data structure of interest. It's structured as
/// an IPC sender that a `ReporterRequest` in transmitted over. `ReporterRequest` objects in turn
/// encapsulate the channel on which the memory profiling information is to be sent.
///
/// In many cases, clients construct `Reporter` objects by creating an IPC sender/receiver pair and
/// registering the receiving end with the router so that messages from the memory profiler end up
/// injected into the client's event loop.
#[derive(Deserialize, Serialize)]
pub struct Reporter(pub IpcSender<ReporterRequest>);

impl Reporter {
    /// Collect one or more memory reports. Returns true on success, and false on failure.
    pub fn collect_reports(&self, reports_chan: ReportsChan) {
        self.0.send(ReporterRequest {
            reports_channel: reports_chan,
        }).unwrap()
    }
}

/// An easy way to build a path for a report.
#[macro_export]
macro_rules! path {
    ($($x:expr),*) => {{
        use std::borrow::ToOwned;
        vec![$( $x.to_owned() ),*]
    }}
}

/// Messages that can be sent to the memory profiler thread.
#[derive(Deserialize, Serialize)]
pub enum ProfilerMsg {
    /// Register a Reporter with the memory profiler. The String is only used to identify the
    /// reporter so it can be unregistered later. The String must be distinct from that used by any
    /// other registered reporter otherwise a panic will occur.
    RegisterReporter(String, Reporter),

    /// Unregister a Reporter with the memory profiler. The String must match the name given when
    /// the reporter was registered. If the String does not match the name of a registered reporter
    /// a panic will occur.
    UnregisterReporter(String),

    /// Triggers printing of the memory profiling metrics.
    Print,

    /// Tells the memory profiler to shut down.
    Exit,
}

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

//! APIs for memory profiling.

#![deny(missing_docs)]

use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use std::marker::Send;
use std::sync::mpsc::Sender;

/// A trait to abstract away the various kinds of message senders we use.
pub trait OpaqueSender<T> {
    /// Send a message.
    fn send(&self, message: T);
}

impl<T> OpaqueSender<T> for Sender<T> {
    fn send(&self, message: T) {
        let _ = Sender::send(self, message);
    }
}

/// Front-end representation of the profiler used to communicate with the
/// profiler.
#[derive(Clone, Deserialize, Serialize)]
pub struct ProfilerChan(pub IpcSender<ProfilerMsg>);

impl ProfilerChan {
    /// Send `msg` on this `IpcSender`.
    ///
    /// Panics if the send fails.
    pub fn send(&self, msg: ProfilerMsg) {
        self.0.send(msg).unwrap();
    }

    /// Runs `f()` with memory profiling.
    pub fn run_with_memory_reporting<F, M, T, C>(&self, f: F,
                                                 reporter_name: String,
                                                 channel_for_reporter: C,
                                                 msg: M)
        where F: FnOnce(),
              M: Fn(ReportsChan) -> T + Send + 'static,
              T: Send + 'static,
              C: OpaqueSender<T> + Send + 'static
    {
        // Register the memory reporter.
        let (reporter_sender, reporter_receiver) = ipc::channel().unwrap();
        ROUTER.add_route(reporter_receiver.to_opaque(), box move |message| {
            // Just injects an appropriate event into the paint thread's queue.
            let request: ReporterRequest = message.to().unwrap();
            channel_for_reporter.send(msg(request.reports_channel));
        });
        self.send(ProfilerMsg::RegisterReporter(reporter_name.clone(),
                                                Reporter(reporter_sender)));

        f();

        self.send(ProfilerMsg::UnregisterReporter(reporter_name));
    }
}

/// The various kinds of memory measurement.
///
/// Here "explicit" means explicit memory allocations done by the application. It includes
/// allocations made at the OS level (via functions such as VirtualAlloc, vm_allocate, and mmap),
/// allocations made at the heap allocation level (via functions such as malloc, calloc, realloc,
/// memalign, operator new, and operator new[]) and where possible, the overhead of the heap
/// allocator itself. It excludes memory that is mapped implicitly such as code and data segments,
/// and thread stacks. "explicit" is not guaranteed to cover every explicit allocation, but it does
/// cover most (including the entire heap), and therefore it is the single best number to focus on
/// when trying to reduce memory usage.
#[derive(Deserialize, Serialize)]
pub enum ReportKind {
    /// A size measurement for an explicit allocation on the jemalloc heap. This should be used
    /// for any measurements done via the `HeapSizeOf` trait.
    ExplicitJemallocHeapSize,

    /// A size measurement for an explicit allocation on the system heap. Only likely to be used
    /// for external C or C++ libraries that don't use jemalloc.
    ExplicitSystemHeapSize,

    /// A size measurement for an explicit allocation not on the heap, e.g. via mmap().
    ExplicitNonHeapSize,

    /// A size measurement for an explicit allocation whose location is unknown or uncertain.
    ExplicitUnknownLocationSize,

    /// A size measurement for a non-explicit allocation. This kind is used for global
    /// measurements such as "resident" and "vsize", and also for measurements that cross-cut the
    /// measurements grouped under "explicit", e.g. by grouping those measurements in a way that's
    /// different to how they are grouped under "explicit".
    NonExplicitSize,
}

/// A single memory-related measurement.
#[derive(Deserialize, Serialize)]
pub struct Report {
    /// The identifying path for this report.
    pub path: Vec<String>,

    /// The report kind.
    pub kind: ReportKind,

    /// The size, in bytes.
    pub size: usize,
}

/// A channel through which memory reports can be sent.
#[derive(Clone, Deserialize, Serialize)]
pub struct ReportsChan(pub IpcSender<Vec<Report>>);

impl ReportsChan {
    /// Send `report` on this `IpcSender`.
    ///
    /// Panics if the send fails.
    pub fn send(&self, report: Vec<Report>) {
        self.0.send(report).unwrap();
    }
}

/// The protocol used to send reporter requests.
#[derive(Deserialize, Serialize)]
pub struct ReporterRequest {
    /// The channel on which reports are to be sent.
    pub reports_channel: ReportsChan,
}

/// A memory reporter is capable of measuring some data structure of interest. It's structured as
/// an IPC sender that a `ReporterRequest` in transmitted over. `ReporterRequest` objects in turn
/// encapsulate the channel on which the memory profiling information is to be sent.
///
/// In many cases, clients construct `Reporter` objects by creating an IPC sender/receiver pair and
/// registering the receiving end with the router so that messages from the memory profiler end up
/// injected into the client's event loop.
#[derive(Deserialize, Serialize)]
pub struct Reporter(pub IpcSender<ReporterRequest>);

impl Reporter {
    /// Collect one or more memory reports. Returns true on success, and false on failure.
    pub fn collect_reports(&self, reports_chan: ReportsChan) {
        self.0.send(ReporterRequest {
            reports_channel: reports_chan,
        }).unwrap()
    }
}

/// An easy way to build a path for a report.
#[macro_export]
macro_rules! path {
    ($($x:expr),*) => {{
        use std::borrow::ToOwned;
        vec![$( $x.to_owned() ),*]
    }}
}

/// Messages that can be sent to the memory profiler thread.
#[derive(Deserialize, Serialize)]
pub enum ProfilerMsg {
    /// Register a Reporter with the memory profiler. The String is only used to identify the
    /// reporter so it can be unregistered later. The String must be distinct from that used by any
    /// other registered reporter otherwise a panic will occur.
    RegisterReporter(String, Reporter),

    /// Unregister a Reporter with the memory profiler. The String must match the name given when
    /// the reporter was registered. If the String does not match the name of a registered reporter
    /// a panic will occur.
    UnregisterReporter(String),

    /// Triggers printing of the memory profiling metrics.
    Print,

    /// Tells the memory profiler to shut down.
    Exit,
}

<|endoftext|>


<commit_before>use std::ptr;

use env::NapiEnv;
use result::NapiResult;
use sys;

mod any;
mod array;
mod boolean;
mod null;
mod number;
mod object;
mod string;
mod undefined;

pub use self::any::NapiAny;
pub use self::array::NapiArray;
pub use self::boolean::NapiBoolean;
pub use self::null::NapiNull;
pub use self::number::NapiNumber;
pub use self::object::NapiObject;
pub use self::string::NapiString;
pub use self::undefined::NapiUndefined;

pub trait NapiValue {
    fn as_sys_value(&self) -> sys::napi_value;
    fn env(&self) -> &NapiEnv;

    fn to_napi_boolean(&self) -> NapiResult<NapiBoolean> {
        coerce(self, sys::napi_coerce_to_bool)
    }

    fn to_napi_number(&self) -> NapiResult<NapiNumber> {
        coerce(self, sys::napi_coerce_to_number)
    }

    fn to_napi_object(&self) -> NapiResult<NapiObject> {
        coerce(self, sys::napi_coerce_to_object)
    }

    fn to_napi_string(&self) -> NapiResult<NapiString> {
        coerce(self, sys::napi_coerce_to_string)
    }

    fn as_any(&self) -> NapiAny {
        NapiAny::with_value(self.env(), self.as_sys_value())
    }

    fn instanceof(&self, constructor: &NapiObject) -> NapiResult<bool> {
        let env = self.env();
        let mut result = false;

        env.handle_status(unsafe {
            sys::napi_instanceof(
                env.as_sys_env(),
                self.as_sys_value(),
                constructor.as_sys_value(),
                &mut result,
            )
        })?;

        Ok(result)
    }

    fn is_array(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_array)
    }

    fn is_arraybuffer(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_arraybuffer)
    }

    fn is_buffer(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_buffer)
    }

    fn is_error(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_error)
    }

    fn is_typedarray(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_typedarray)
    }

    fn is_dataview(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_dataview)
    }

    fn strict_equals<T>(&self, other: &T) -> NapiResult<bool>
    where
        T: NapiValue + ?Sized,
    {
        let env = self.env();
        let mut result = false;

        env.handle_status(unsafe {
            sys::napi_strict_equals(
                env.as_sys_env(),
                self.as_sys_value(),
                other.as_sys_value(),
                &mut result,
            )
        })?;

        Ok(result)
    }
}

trait NapiValueInternal<'a>: NapiValue + 'a {
    fn construct(env: &'a NapiEnv, value: sys::napi_value) -> Self;
}

fn coerce<'a, T, U>(
    value: &'a T,
    napi_fn: unsafe extern "C" fn(
        sys::napi_env,
        sys::napi_value,
        *mut sys::napi_value,
    ) -> sys::napi_status,
) -> NapiResult<U>
where
    T: NapiValue + ?Sized,
    U: NapiValueInternal<'a>,
{
    let env = value.env();
    let mut coerced_value = ptr::null_mut();

    env.handle_status(unsafe {
        napi_fn(env.as_sys_env(), value.as_sys_value(), &mut coerced_value)
    })?;

    Ok(U::construct(env, coerced_value))
}

fn check_type<'a, T>(
    value: &'a T,
    napi_fn: unsafe extern "C" fn(sys::napi_env, sys::napi_value, *mut bool)
        -> sys::napi_status,
) -> NapiResult<bool>
where
    T: NapiValue + ?Sized,
{
    let env = value.env();
    let mut result = false;

    env.handle_status(unsafe {
        napi_fn(env.as_sys_env(), value.as_sys_value(), &mut result)
    })?;

    Ok(result)
}
<commit_msg>fix(napi): rename NapiValue::as_any to NapiValue::as_napi_any<commit_after>use std::ptr;

use env::NapiEnv;
use result::NapiResult;
use sys;

mod any;
mod array;
mod boolean;
mod null;
mod number;
mod object;
mod string;
mod undefined;

pub use self::any::NapiAny;
pub use self::array::NapiArray;
pub use self::boolean::NapiBoolean;
pub use self::null::NapiNull;
pub use self::number::NapiNumber;
pub use self::object::NapiObject;
pub use self::string::NapiString;
pub use self::undefined::NapiUndefined;

pub trait NapiValue {
    fn as_sys_value(&self) -> sys::napi_value;
    fn env(&self) -> &NapiEnv;

    fn to_napi_boolean(&self) -> NapiResult<NapiBoolean> {
        coerce(self, sys::napi_coerce_to_bool)
    }

    fn to_napi_number(&self) -> NapiResult<NapiNumber> {
        coerce(self, sys::napi_coerce_to_number)
    }

    fn to_napi_object(&self) -> NapiResult<NapiObject> {
        coerce(self, sys::napi_coerce_to_object)
    }

    fn to_napi_string(&self) -> NapiResult<NapiString> {
        coerce(self, sys::napi_coerce_to_string)
    }

    fn as_napi_any(&self) -> NapiAny {
        NapiAny::with_value(self.env(), self.as_sys_value())
    }

    fn instanceof(&self, constructor: &NapiObject) -> NapiResult<bool> {
        let env = self.env();
        let mut result = false;

        env.handle_status(unsafe {
            sys::napi_instanceof(
                env.as_sys_env(),
                self.as_sys_value(),
                constructor.as_sys_value(),
                &mut result,
            )
        })?;

        Ok(result)
    }

    fn is_array(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_array)
    }

    fn is_arraybuffer(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_arraybuffer)
    }

    fn is_buffer(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_buffer)
    }

    fn is_error(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_error)
    }

    fn is_typedarray(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_typedarray)
    }

    fn is_dataview(&self) -> NapiResult<bool> {
        check_type(self, sys::napi_is_dataview)
    }

    fn strict_equals<T>(&self, other: &T) -> NapiResult<bool>
    where
        T: NapiValue + ?Sized,
    {
        let env = self.env();
        let mut result = false;

        env.handle_status(unsafe {
            sys::napi_strict_equals(
                env.as_sys_env(),
                self.as_sys_value(),
                other.as_sys_value(),
                &mut result,
            )
        })?;

        Ok(result)
    }
}

trait NapiValueInternal<'a>: NapiValue + 'a {
    fn construct(env: &'a NapiEnv, value: sys::napi_value) -> Self;
}

fn coerce<'a, T, U>(
    value: &'a T,
    napi_fn: unsafe extern "C" fn(
        sys::napi_env,
        sys::napi_value,
        *mut sys::napi_value,
    ) -> sys::napi_status,
) -> NapiResult<U>
where
    T: NapiValue + ?Sized,
    U: NapiValueInternal<'a>,
{
    let env = value.env();
    let mut coerced_value = ptr::null_mut();

    env.handle_status(unsafe {
        napi_fn(env.as_sys_env(), value.as_sys_value(), &mut coerced_value)
    })?;

    Ok(U::construct(env, coerced_value))
}

fn check_type<'a, T>(
    value: &'a T,
    napi_fn: unsafe extern "C" fn(sys::napi_env, sys::napi_value, *mut bool)
        -> sys::napi_status,
) -> NapiResult<bool>
where
    T: NapiValue + ?Sized,
{
    let env = value.env();
    let mut result = false;

    env.handle_status(unsafe {
        napi_fn(env.as_sys_env(), value.as_sys_value(), &mut result)
    })?;

    Ok(result)
}
<|endoftext|>


<commit_before>pub struct Zero;
pub struct Succ<T: NonNeg>;
pub struct Pred<T: NonPos>;

pub type One = Succ<Zero>;

pub trait Peano {}
pub trait NonZero: Peano {}
pub trait NonNeg: Peano {}
pub trait NonPos: Peano {}
pub trait Pos: Peano + NonZero + NonNeg {}
pub trait Neg: Peano + NonZero + NonPos {}

impl Peano for Zero {}
impl NonNeg for Zero {}
impl NonPos for Zero {}

impl<T: NonNeg> Peano for Succ<T> {}
impl<T: NonNeg> NonNeg for Succ<T> {}
impl<T: NonNeg> NonZero for Succ<T> {}
impl<T: NonNeg> Pos for Succ<T> {}

impl<T: NonPos> Peano for Pred<T> {}
impl<T: NonPos> NonPos for Pred<T> {}
impl<T: NonPos> NonZero for Pred<T> {}
impl<T: NonPos> Neg for Pred<T> {}

impl Copy for Zero {}

pub trait AddPeano<Rhs> {
    type Result;
}

/// Adding things to zero (e.g. 0 + 3)
impl<Rhs: Peano> AddPeano<Rhs> for Zero {
    type Result = Rhs;
}

/// Adding positive numbers (e.g. 1 + 2)
impl<T: NonNeg + AddPeano<Succ<Rhs>>, Rhs: NonNeg> AddPeano<Succ<Rhs>> for Succ<T> {
    type Result = Succ<<T as AddPeano<Succ<Rhs>>>::Result>;
}
/// Adding zero to positive numbers (e.g. 3 + 0)
impl<T: NonNeg> AddPeano<Zero> for Succ<T> {
    type Result = Succ<T>;
}
/// Adding negative numbers to positive numbers (e.g. 2 + -3)
impl<T: NonNeg + AddPeano<Rhs>, Rhs: NonPos> AddPeano<Pred<Rhs>> for Succ<T> {
    type Result = <T as AddPeano<Rhs>>::Result;
}

/// Adding negative numbers (e.g. -1 + -2)
impl<T: NonPos + AddPeano<Pred<Rhs>>, Rhs: NonPos> AddPeano<Pred<Rhs>> for Pred<T> {
    type Result = Pred<<T as AddPeano<Pred<Rhs>>>::Result>;
}
/// Adding zero to negative numbers (e.g. -3 + 0)
impl<T: NonPos> AddPeano<Zero> for Pred<T> {
    type Result = Pred<T>;
}
/// Adding positive numbers to negative numbers (e.g. -2 + 3)
impl<T: NonPos + AddPeano<Rhs>, Rhs: NonNeg> AddPeano<Succ<Rhs>> for Pred<T> {
    type Result = <T as AddPeano<Rhs>>::Result;
}

pub trait Negate {
    type Result;
}
impl Negate for Zero {
    type Result = Zero;
}
impl<T: NonNeg + Negate> Negate for Succ<T> {
    type Result = Pred<<T as Negate>::Result>;
}
impl<T: NonPos + Negate> Negate for Pred<T> {
    type Result = Succ<<T as Negate>::Result>;
}

pub trait SubPeano<Rhs> {
    type Result;
}

/// Subtracting from zero
impl<Rhs: Peano + Negate> SubPeano<Rhs> for Zero {
    type Result = <Rhs as Negate>::Result;
}

/// Subtracting positive numbers from positive numbers (e.g. 3 - 4)
impl<T: NonNeg + SubPeano<Rhs>, Rhs: NonNeg> SubPeano<Succ<Rhs>> for Succ<T> {
    type Result = <T as SubPeano<Rhs>>::Result;
}
/// Subtracting zero from positive numbers (e.g. 3 - 0)
impl<T: NonNeg> SubPeano<Zero> for Succ<T> {
    type Result = Succ<T>;
}
/// Subtracting negative numbers from positive numbers (e.g. 3 - -4)
impl<T: NonNeg + SubPeano<Rhs>, Rhs: NonPos> SubPeano<Pred<Rhs>> for Succ<T> {
    type Result = Succ<Succ<<T as SubPeano<Rhs>>::Result>>;
}

/// Subtracting positive numbers from negative numbers (e.g. -3 - 4)
impl<T: NonPos + SubPeano<Rhs>, Rhs: NonNeg> SubPeano<Succ<Rhs>> for Pred<T> {
    type Result = Pred<Pred<<T as SubPeano<Rhs>>::Result>>;
}
/// Subtracting zero from negative numbers (e.g. -3 - 0)
impl<T: NonPos> SubPeano<Zero> for Pred<T> {
    type Result = Pred<T>;
}
/// Subtracting negative numbers from negative numbers (e.g. -3 - -4)
impl<T: NonPos + SubPeano<Rhs>, Rhs: NonPos> SubPeano<Pred<Rhs>> for Pred<T> {
    type Result = <T as SubPeano<Rhs>>::Result;
}

pub trait MulPeano<Rhs> {
    type Result;
}

/// Multiplying zero by things (e.g. 0 * 7)
impl<Rhs: Peano> MulPeano<Rhs> for Zero {
    type Result = Zero;
}

/// Multiplying a positive integer by an arbitrary integer (e.g. 2 * N)
impl<T, Rhs> MulPeano<Rhs> for Succ<T>
    where T: NonNeg + MulPeano<Rhs>, Rhs: AddPeano<<T as MulPeano<Rhs>>::Result> {
        type Result = <Rhs as AddPeano<<T as MulPeano<Rhs>>::Result>>::Result;
}

/// Multiplying a negative integer by an arbitrary integer (e.g. -2 * N)
impl<T, Rhs> MulPeano<Rhs> for Pred<T>
    where T: NonPos + MulPeano<Rhs>, Rhs: Peano, <T as MulPeano<Rhs>>::Result: SubPeano<Rhs> {
        type Result = <<T as MulPeano<Rhs>>::Result as SubPeano<Rhs>>::Result;
}

trait ToInt {
    fn to_int() -> i32;
}
impl ToInt for Zero {
    fn to_int() -> i32 { 0 }
}
impl<T:NonNeg + ToInt> ToInt for Succ<T> {
    fn to_int() -> i32 { 1 + <T as ToInt>::to_int() }
}
impl<T:NonPos + ToInt> ToInt for Pred<T> {
    fn to_int() -> i32 { -1 + <T as ToInt>::to_int() }
}

#[test]
fn test_peano() {
    type Two = Succ<One>;
    type Three = Succ<Two>;

    type NegOne = Pred<Zero>;
    type NegTwo = Pred<NegOne>;
    type NegThree = Pred<NegTwo>;

    // Testing equality
    // 0 == 0
    assert_eq!( 0, <Zero as ToInt>::to_int() );
    // 2 == 2
    assert_eq!( 2, <Two as ToInt>::to_int() );
    // -2 == -2
    assert_eq!( -2, <NegTwo as ToInt>::to_int() );

    // Testing addition
    // 0 + 0 == 0
    assert_eq!( 0, <<Zero as AddPeano<Zero>>::Result as ToInt>::to_int() );
    // 0 + 3 == 3
    assert_eq!( 3, <<Zero as AddPeano<Three>>::Result as ToInt>::to_int() );
    // 0 + -3 == -3
    assert_eq!( -3, <<Zero as AddPeano<NegThree>>::Result as ToInt>::to_int() );

    // 2 + 0 == 2
    assert_eq!( 2, <<Two as AddPeano<Zero>>::Result as ToInt>::to_int() );
    // 2 + 3 == 5
    assert_eq!( 5, <<Two as AddPeano<Three>>::Result as ToInt>::to_int() );
    // 2 + -3 == -1
    assert_eq!( -1, <<Two as AddPeano<NegThree>>::Result as ToInt>::to_int() );
    // 3 + -2 == 1
    assert_eq!( 1, <<Three as AddPeano<NegTwo>>::Result as ToInt>::to_int() );

    // -2 + 0 == 2
    assert_eq!( -2, <<NegTwo as AddPeano<Zero>>::Result as ToInt>::to_int() );
    // -2 + -3 == -5
    assert_eq!( -5, <<NegTwo as AddPeano<NegThree>>::Result as ToInt>::to_int() );
    // -2 + 3 == 1
    assert_eq!( 1, <<NegTwo as AddPeano<Three>>::Result as ToInt>::to_int() );
    // -3 + 2 == -1
    assert_eq!( -1, <<NegThree as AddPeano<Two>>::Result as ToInt>::to_int() );

    // Testing Negation
    // -3 == -(3)
    assert_eq!( -3, <<Three as Negate>::Result as ToInt>::to_int() );
    // 3 == -(-3)
    assert_eq!( 3, <<NegThree as Negate>::Result as ToInt>::to_int() );
    // 0 == -0
    assert_eq!( 0, <<Zero as Negate>::Result as ToInt>::to_int() );

    // Testing Subtraction
    // 0 - 0 == 0
    assert_eq!( 0, <<Zero as SubPeano<Zero>>::Result as ToInt>::to_int() );
    // 0 - 3 == -3
    assert_eq!( -3, <<Zero as SubPeano<Three>>::Result as ToInt>::to_int() );
    // 0 - -3 == 3
    assert_eq!( 3, <<Zero as SubPeano<NegThree>>::Result as ToInt>::to_int() );

    // 2 - 0 == 2
    assert_eq!( 2, <<Two as SubPeano<Zero>>::Result as ToInt>::to_int() );
    // 2 - 3 == -1
    assert_eq!( -1, <<Two as SubPeano<Three>>::Result as ToInt>::to_int() );
    // 2 - -3 == 5
    assert_eq!( 5, <<Two as SubPeano<NegThree>>::Result as ToInt>::to_int() );
    // 3 - -2 == 5
    assert_eq!( 5, <<Three as SubPeano<NegTwo>>::Result as ToInt>::to_int() );

    // -2 - 0 == -2
    assert_eq!( -2, <<NegTwo as SubPeano<Zero>>::Result as ToInt>::to_int() );
    // -2 - -3 == 1
    assert_eq!( 1, <<NegTwo as SubPeano<NegThree>>::Result as ToInt>::to_int() );
    // -2 - 3 == -5
    assert_eq!( -5, <<NegTwo as SubPeano<Three>>::Result as ToInt>::to_int() );
    // -3 - 2 == -5
    assert_eq!( -5, <<NegThree as SubPeano<Two>>::Result as ToInt>::to_int() );

    // Testing Multiplication
    // 0 * 0 == 0
    assert_eq!( 0, <<Zero as MulPeano<Zero>>::Result as ToInt>::to_int() );
    // 0 * 2 == 0
    assert_eq!( 0, <<Zero as MulPeano<Two>>::Result as ToInt>::to_int() );
    // 2 * 0 == 0
    assert_eq!( 0, <<Two as MulPeano<Zero>>::Result as ToInt>::to_int() );

    // 2 * 3 == 6
    assert_eq!( 6, <<Two as MulPeano<Three>>::Result as ToInt>::to_int() );
    // 2 * -3 == -6
    assert_eq!( -6, <<Two as MulPeano<NegThree>>::Result as ToInt>::to_int() );
    // -2 * 3 == -6
    assert_eq!( -6, <<NegTwo as MulPeano<Three>>::Result as ToInt>::to_int() );
    // -2 * -3 == 6
    assert_eq!( 6, <<NegTwo as MulPeano<NegThree>>::Result as ToInt>::to_int() );
}
<commit_msg>Result -> Output in Peano traits to be consistent with built-in arithmetic traits<commit_after>pub struct Zero;
pub struct Succ<T: NonNeg>;
pub struct Pred<T: NonPos>;

pub type One = Succ<Zero>;

pub trait Peano {}
pub trait NonZero: Peano {}
pub trait NonNeg: Peano {}
pub trait NonPos: Peano {}
pub trait Pos: Peano + NonZero + NonNeg {}
pub trait Neg: Peano + NonZero + NonPos {}

impl Peano for Zero {}
impl NonNeg for Zero {}
impl NonPos for Zero {}

impl<T: NonNeg> Peano for Succ<T> {}
impl<T: NonNeg> NonNeg for Succ<T> {}
impl<T: NonNeg> NonZero for Succ<T> {}
impl<T: NonNeg> Pos for Succ<T> {}

impl<T: NonPos> Peano for Pred<T> {}
impl<T: NonPos> NonPos for Pred<T> {}
impl<T: NonPos> NonZero for Pred<T> {}
impl<T: NonPos> Neg for Pred<T> {}

impl Copy for Zero {}

pub trait AddPeano<Rhs> {
    type Output;
}

/// Adding things to zero (e.g. 0 + 3)
impl<Rhs: Peano> AddPeano<Rhs> for Zero {
    type Output = Rhs;
}

/// Adding positive numbers (e.g. 1 + 2)
impl<T: NonNeg + AddPeano<Succ<Rhs>>, Rhs: NonNeg> AddPeano<Succ<Rhs>> for Succ<T> {
    type Output = Succ<<T as AddPeano<Succ<Rhs>>>::Output>;
}
/// Adding zero to positive numbers (e.g. 3 + 0)
impl<T: NonNeg> AddPeano<Zero> for Succ<T> {
    type Output = Succ<T>;
}
/// Adding negative numbers to positive numbers (e.g. 2 + -3)
impl<T: NonNeg + AddPeano<Rhs>, Rhs: NonPos> AddPeano<Pred<Rhs>> for Succ<T> {
    type Output = <T as AddPeano<Rhs>>::Output;
}

/// Adding negative numbers (e.g. -1 + -2)
impl<T: NonPos + AddPeano<Pred<Rhs>>, Rhs: NonPos> AddPeano<Pred<Rhs>> for Pred<T> {
    type Output = Pred<<T as AddPeano<Pred<Rhs>>>::Output>;
}
/// Adding zero to negative numbers (e.g. -3 + 0)
impl<T: NonPos> AddPeano<Zero> for Pred<T> {
    type Output = Pred<T>;
}
/// Adding positive numbers to negative numbers (e.g. -2 + 3)
impl<T: NonPos + AddPeano<Rhs>, Rhs: NonNeg> AddPeano<Succ<Rhs>> for Pred<T> {
    type Output = <T as AddPeano<Rhs>>::Output;
}

pub trait Negate {
    type Output;
}
impl Negate for Zero {
    type Output = Zero;
}
impl<T: NonNeg + Negate> Negate for Succ<T> {
    type Output = Pred<<T as Negate>::Output>;
}
impl<T: NonPos + Negate> Negate for Pred<T> {
    type Output = Succ<<T as Negate>::Output>;
}

pub trait SubPeano<Rhs> {
    type Output;
}

/// Subtracting from zero
impl<Rhs: Peano + Negate> SubPeano<Rhs> for Zero {
    type Output = <Rhs as Negate>::Output;
}

/// Subtracting positive numbers from positive numbers (e.g. 3 - 4)
impl<T: NonNeg + SubPeano<Rhs>, Rhs: NonNeg> SubPeano<Succ<Rhs>> for Succ<T> {
    type Output = <T as SubPeano<Rhs>>::Output;
}
/// Subtracting zero from positive numbers (e.g. 3 - 0)
impl<T: NonNeg> SubPeano<Zero> for Succ<T> {
    type Output = Succ<T>;
}
/// Subtracting negative numbers from positive numbers (e.g. 3 - -4)
impl<T: NonNeg + SubPeano<Rhs>, Rhs: NonPos> SubPeano<Pred<Rhs>> for Succ<T> {
    type Output = Succ<Succ<<T as SubPeano<Rhs>>::Output>>;
}

/// Subtracting positive numbers from negative numbers (e.g. -3 - 4)
impl<T: NonPos + SubPeano<Rhs>, Rhs: NonNeg> SubPeano<Succ<Rhs>> for Pred<T> {
    type Output = Pred<Pred<<T as SubPeano<Rhs>>::Output>>;
}
/// Subtracting zero from negative numbers (e.g. -3 - 0)
impl<T: NonPos> SubPeano<Zero> for Pred<T> {
    type Output = Pred<T>;
}
/// Subtracting negative numbers from negative numbers (e.g. -3 - -4)
impl<T: NonPos + SubPeano<Rhs>, Rhs: NonPos> SubPeano<Pred<Rhs>> for Pred<T> {
    type Output = <T as SubPeano<Rhs>>::Output;
}

pub trait MulPeano<Rhs> {
    type Output;
}

/// Multiplying zero by things (e.g. 0 * 7)
impl<Rhs: Peano> MulPeano<Rhs> for Zero {
    type Output = Zero;
}

/// Multiplying a positive integer by an arbitrary integer (e.g. 2 * N)
impl<T, Rhs> MulPeano<Rhs> for Succ<T>
    where T: NonNeg + MulPeano<Rhs>, Rhs: AddPeano<<T as MulPeano<Rhs>>::Output> {
        type Output = <Rhs as AddPeano<<T as MulPeano<Rhs>>::Output>>::Output;
}

/// Multiplying a negative integer by an arbitrary integer (e.g. -2 * N)
impl<T, Rhs> MulPeano<Rhs> for Pred<T>
    where T: NonPos + MulPeano<Rhs>, Rhs: Peano, <T as MulPeano<Rhs>>::Output: SubPeano<Rhs> {
        type Output = <<T as MulPeano<Rhs>>::Output as SubPeano<Rhs>>::Output;
}

trait ToInt {
    fn to_int() -> i32;
}
impl ToInt for Zero {
    fn to_int() -> i32 { 0 }
}
impl<T:NonNeg + ToInt> ToInt for Succ<T> {
    fn to_int() -> i32 { 1 + <T as ToInt>::to_int() }
}
impl<T:NonPos + ToInt> ToInt for Pred<T> {
    fn to_int() -> i32 { -1 + <T as ToInt>::to_int() }
}

#[test]
fn test_peano() {
    type Two = Succ<One>;
    type Three = Succ<Two>;

    type NegOne = Pred<Zero>;
    type NegTwo = Pred<NegOne>;
    type NegThree = Pred<NegTwo>;

    // Testing equality
    // 0 == 0
    assert_eq!( 0, <Zero as ToInt>::to_int() );
    // 2 == 2
    assert_eq!( 2, <Two as ToInt>::to_int() );
    // -2 == -2
    assert_eq!( -2, <NegTwo as ToInt>::to_int() );

    // Testing addition
    // 0 + 0 == 0
    assert_eq!( 0, <<Zero as AddPeano<Zero>>::Output as ToInt>::to_int() );
    // 0 + 3 == 3
    assert_eq!( 3, <<Zero as AddPeano<Three>>::Output as ToInt>::to_int() );
    // 0 + -3 == -3
    assert_eq!( -3, <<Zero as AddPeano<NegThree>>::Output as ToInt>::to_int() );

    // 2 + 0 == 2
    assert_eq!( 2, <<Two as AddPeano<Zero>>::Output as ToInt>::to_int() );
    // 2 + 3 == 5
    assert_eq!( 5, <<Two as AddPeano<Three>>::Output as ToInt>::to_int() );
    // 2 + -3 == -1
    assert_eq!( -1, <<Two as AddPeano<NegThree>>::Output as ToInt>::to_int() );
    // 3 + -2 == 1
    assert_eq!( 1, <<Three as AddPeano<NegTwo>>::Output as ToInt>::to_int() );

    // -2 + 0 == 2
    assert_eq!( -2, <<NegTwo as AddPeano<Zero>>::Output as ToInt>::to_int() );
    // -2 + -3 == -5
    assert_eq!( -5, <<NegTwo as AddPeano<NegThree>>::Output as ToInt>::to_int() );
    // -2 + 3 == 1
    assert_eq!( 1, <<NegTwo as AddPeano<Three>>::Output as ToInt>::to_int() );
    // -3 + 2 == -1
    assert_eq!( -1, <<NegThree as AddPeano<Two>>::Output as ToInt>::to_int() );

    // Testing Negation
    // -3 == -(3)
    assert_eq!( -3, <<Three as Negate>::Output as ToInt>::to_int() );
    // 3 == -(-3)
    assert_eq!( 3, <<NegThree as Negate>::Output as ToInt>::to_int() );
    // 0 == -0
    assert_eq!( 0, <<Zero as Negate>::Output as ToInt>::to_int() );

    // Testing Subtraction
    // 0 - 0 == 0
    assert_eq!( 0, <<Zero as SubPeano<Zero>>::Output as ToInt>::to_int() );
    // 0 - 3 == -3
    assert_eq!( -3, <<Zero as SubPeano<Three>>::Output as ToInt>::to_int() );
    // 0 - -3 == 3
    assert_eq!( 3, <<Zero as SubPeano<NegThree>>::Output as ToInt>::to_int() );

    // 2 - 0 == 2
    assert_eq!( 2, <<Two as SubPeano<Zero>>::Output as ToInt>::to_int() );
    // 2 - 3 == -1
    assert_eq!( -1, <<Two as SubPeano<Three>>::Output as ToInt>::to_int() );
    // 2 - -3 == 5
    assert_eq!( 5, <<Two as SubPeano<NegThree>>::Output as ToInt>::to_int() );
    // 3 - -2 == 5
    assert_eq!( 5, <<Three as SubPeano<NegTwo>>::Output as ToInt>::to_int() );

    // -2 - 0 == -2
    assert_eq!( -2, <<NegTwo as SubPeano<Zero>>::Output as ToInt>::to_int() );
    // -2 - -3 == 1
    assert_eq!( 1, <<NegTwo as SubPeano<NegThree>>::Output as ToInt>::to_int() );
    // -2 - 3 == -5
    assert_eq!( -5, <<NegTwo as SubPeano<Three>>::Output as ToInt>::to_int() );
    // -3 - 2 == -5
    assert_eq!( -5, <<NegThree as SubPeano<Two>>::Output as ToInt>::to_int() );

    // Testing Multiplication
    // 0 * 0 == 0
    assert_eq!( 0, <<Zero as MulPeano<Zero>>::Output as ToInt>::to_int() );
    // 0 * 2 == 0
    assert_eq!( 0, <<Zero as MulPeano<Two>>::Output as ToInt>::to_int() );
    // 2 * 0 == 0
    assert_eq!( 0, <<Two as MulPeano<Zero>>::Output as ToInt>::to_int() );

    // 2 * 3 == 6
    assert_eq!( 6, <<Two as MulPeano<Three>>::Output as ToInt>::to_int() );
    // 2 * -3 == -6
    assert_eq!( -6, <<Two as MulPeano<NegThree>>::Output as ToInt>::to_int() );
    // -2 * 3 == -6
    assert_eq!( -6, <<NegTwo as MulPeano<Three>>::Output as ToInt>::to_int() );
    // -2 * -3 == 6
    assert_eq!( 6, <<NegTwo as MulPeano<NegThree>>::Output as ToInt>::to_int() );
}
<|endoftext|>