<commit_before>// ----------------------------------------------------------------------
// Gathering loans
//
// The borrow check proceeds in two phases. In phase one, we gather the full
// set of loans that are required at any point.  These are sorted according to
// their associated scopes.  In phase two, checking loans, we will then make
// sure that all of these loans are honored.

import categorization::{public_methods, opt_deref_kind};
import loan::public_methods;
import preserve::public_methods;

export gather_loans;

enum gather_loan_ctxt = @{bccx: borrowck_ctxt, req_maps: req_maps};

fn gather_loans(bccx: borrowck_ctxt, crate: @ast::crate) -> req_maps {
    let glcx = gather_loan_ctxt(@{bccx: bccx,
                                  req_maps: {req_loan_map: int_hash(),
                                             pure_map: int_hash()}});
    let v = visit::mk_vt(@{visit_expr: req_loans_in_expr
                           with *visit::default_visitor()});
    visit::visit_crate(*crate, glcx, v);
    ret glcx.req_maps;
}

fn req_loans_in_expr(ex: @ast::expr,
                     &&self: gather_loan_ctxt,
                     vt: visit::vt<gather_loan_ctxt>) {
    let bccx = self.bccx;
    let tcx = bccx.tcx;

    #debug["req_loans_in_expr(ex=%s)", pprust::expr_to_str(ex)];

    // If this expression is borrowed, have to ensure it remains valid:
    for tcx.borrowings.find(ex.id).each { |borrow|
        let cmt = self.bccx.cat_borrow_of_expr(ex);
        let scope_r = ty::re_scope(borrow.scope_id);
        self.guarantee_valid(cmt, borrow.mutbl, scope_r);
    }

    // Special checks for various kinds of expressions:
    alt ex.node {
      ast::expr_addr_of(mutbl, base) {
        let base_cmt = self.bccx.cat_expr(base);

        // make sure that the thing we are pointing out stays valid
        // for the lifetime `scope_r` of the resulting ptr:
        let scope_r =
            alt check ty::get(tcx.ty(ex)).struct {
              ty::ty_rptr(r, _) { r }
            };
        self.guarantee_valid(base_cmt, mutbl, scope_r);
      }

      ast::expr_call(f, args, _) {
        let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
        let scope_r = ty::re_scope(ex.id);
        vec::iter2(args, arg_tys) { |arg, arg_ty|
            alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
              ast::by_mutbl_ref {
                let arg_cmt = self.bccx.cat_expr(arg);
                self.guarantee_valid(arg_cmt, m_mutbl, scope_r);
              }
              ast::by_ref {
                let arg_cmt = self.bccx.cat_expr(arg);
                self.guarantee_valid(arg_cmt, m_imm,  scope_r);
              }
              ast::by_val {
                // Rust's by-val does not actually give ownership to
                // the callee.  This means that if a pointer type is
                // passed, it is effectively a borrow, and so the
                // caller must guarantee that the data remains valid.
                //
                // Subtle: we only guarantee that the pointer is valid
                // and const.  Technically, we ought to pass in the
                // mutability that the caller expects (e.g., if the
                // formal argument has type @mut, we should guarantee
                // validity and mutability, not validity and const).
                // However, the type system already guarantees that
                // the caller's mutability is compatible with the
                // callee, so this is not necessary.  (Note that with
                // actual borrows, typeck is more liberal and allows
                // the pointer to be borrowed as immutable even if it
                // is mutable in the caller's frame, thus effectively
                // passing the buck onto us to enforce this)

                alt opt_deref_kind(arg_ty.ty) {
                  some(deref_ptr(region_ptr)) {
                    /* region pointers are (by induction) guaranteed */
                  }
                  none {
                    /* not a pointer, no worries */
                  }
                  some(_) {
                    let arg_cmt = self.bccx.cat_borrow_of_expr(arg);
                    self.guarantee_valid(arg_cmt, m_const, scope_r);
                  }
                }
              }
              ast::by_move | ast::by_copy {}
            }
        }
      }

      ast::expr_alt(ex_v, arms, _) {
        let cmt = self.bccx.cat_expr(ex_v);
        for arms.each { |arm|
            for arm.pats.each { |pat|
                self.gather_pat(cmt, pat, arm.body.node.id, ex.id);
            }
        }
      }

      ast::expr_index(rcvr, _) |
      ast::expr_binary(_, rcvr, _) |
      ast::expr_unary(_, rcvr) if self.bccx.method_map.contains_key(ex.id) {
        // Receivers in method calls are always passed by ref.
        //
        // Here, in an overloaded operator, the call is this expression,
        // and hence the scope of the borrow is this call.
        //
        // FIXME/NOT REALLY---technically we should check the other
        // argument and consider the argument mode.  But how annoying.
        // And this problem when goes away when argument modes are
        // phased out.  So I elect to leave this undone.
        let scope_r = ty::re_scope(ex.id);
        let rcvr_cmt = self.bccx.cat_expr(rcvr);
        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
      }

      ast::expr_field(rcvr, _, _)
      if self.bccx.method_map.contains_key(ex.id) {
        // Receivers in method calls are always passed by ref.
        //
        // Here, the field a.b is in fact a closure.  Eventually, this
        // should be an fn&, but for now it's an fn@.  In any case,
        // the enclosing scope is either the call where it is a rcvr
        // (if used like `a.b(...)`), the call where it's an argument
        // (if used like `x(a.b)`), or the block (if used like `let x
        // = a.b`).
        let scope_r = ty::re_scope(self.tcx().region_map.get(ex.id));
        let rcvr_cmt = self.bccx.cat_expr(rcvr);
        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
      }

      _ { /*ok*/ }
    }

    // Check any contained expressions:
    visit::visit_expr(ex, self, vt);
}

impl methods for gather_loan_ctxt {
    fn tcx() -> ty::ctxt { self.bccx.tcx }

    // guarantees that addr_of(cmt) will be valid for the duration of
    // `static_scope_r`, or reports an error.  This may entail taking
    // out loans, which will be added to the `req_loan_map`.  This can
    // also entail "rooting" GC'd pointers, which means ensuring
    // dynamically that they are not freed.
    fn guarantee_valid(cmt: cmt,
                       req_mutbl: ast::mutability,
                       scope_r: ty::region) {

        #debug["guarantee_valid(cmt=%s, req_mutbl=%s, scope_r=%s)",
               self.bccx.cmt_to_repr(cmt),
               self.bccx.mut_to_str(req_mutbl),
               region_to_str(self.tcx(), scope_r)];
        let _i = indenter();

        alt cmt.lp {
          // If this expression is a loanable path, we MUST take out a loan.
          // This is somewhat non-obvious.  You might think, for example, that
          // if we have an immutable local variable `x` whose value is being
          // borrowed, we could rely on `x` not to change.  This is not so,
          // however, because even immutable locals can be moved.  So we take
          // out a loan on `x`, guaranteeing that it remains immutable for the
          // duration of the reference: if there is an attempt to move it
          // within that scope, the loan will be detected and an error will be
          // reported.
          some(_) {
            alt scope_r {
              ty::re_scope(scope_id) {
                let loans = self.bccx.loan(cmt, req_mutbl);
                self.add_loans(scope_id, loans);
              }
              _ {
                self.bccx.span_err(
                    cmt.span,
                    #fmt["cannot guarantee the stability \
                          of this expression for the entirety of \
                          its lifetime, %s",
                         region_to_str(self.tcx(), scope_r)]);
              }
            }
          }

          // The path is not loanable: in that case, we must try and preserve
          // it dynamically (or see that it is preserved by virtue of being
          // rooted in some immutable path)
          none {
            let opt_scope_id = alt scope_r {
              ty::re_scope(scope_id) { some(scope_id) }
              _ { none }
            };

            let result = {
                self.check_mutbl(req_mutbl, cmt).chain { |_ok|
                    self.bccx.preserve(cmt, opt_scope_id)
                }
            };

            alt result {
              ok(()) {
                // we were able guarantee the validity of the ptr,
                // perhaps by rooting or because it is immutably
                // rooted.  good.
              }
              err(e) {
                // not able to guarantee the validity of the ptr.
                // rather than report an error, presuming that the
                // borrow is for a limited scope, we'll make one last
                // ditch effort and require that the scope where the
                // borrow occurs be pure.
                alt opt_scope_id {
                  some(scope_id) {
                    self.req_maps.pure_map.insert(scope_id, e);
                  }
                  none {
                    // otherwise, fine, I give up.
                    self.bccx.report(e);
                  }
                }
              }
            }
          }
        }
    }

    // Check that the pat `cmt` is compatible with the required
    // mutability, presuming that it can be preserved to stay alive
    // long enough.
    //
    // For example, if you have an expression like `&x.f` where `x`
    // has type `@mut{f:int}`, this check might fail because `&x.f`
    // reqires an immutable pointer, but `f` lives in (aliased)
    // mutable memory.
    fn check_mutbl(req_mutbl: ast::mutability,
                   cmt: cmt) -> bckres<()> {
        alt (req_mutbl, cmt.mutbl) {
          (m_const, _) |
          (m_imm, m_imm) |
          (m_mutbl, m_mutbl) {
            ok(())
          }

          (_, m_const) |
          (m_imm, m_mutbl) |
          (m_mutbl, m_imm) {
            err({cmt: cmt,
                 code: err_mutbl(req_mutbl, cmt.mutbl)})
          }
        }
    }

    fn add_loans(scope_id: ast::node_id, loans: @const [loan]) {
        alt self.req_maps.req_loan_map.find(scope_id) {
          some(l) {
            *l += [loans];
          }
          none {
            self.req_maps.req_loan_map.insert(scope_id, @mut [loans]);
          }
        }
    }

    fn gather_pat(cmt: cmt, pat: @ast::pat,
                  arm_id: ast::node_id, alt_id: ast::node_id) {

        // Here, `cmt` is the categorization for the value being
        // matched and pat is the pattern it is being matched against.
        //
        // In general, the way that this works is that we walk down
        // the pattern, constructing a cmt that represents the path
        // that will be taken to reach the value being matched.
        //
        // When we encounter named bindings, we take the cmt that has
        // been built up and pass it off to guarantee_valid() so that
        // we can be sure that the binding will remain valid for the
        // duration of the arm.
        //
        // The correspondence between the id in the cmt and which
        // pattern is being referred to is somewhat...subtle.  In
        // general, the id of the cmt is the id of the node that
        // produces the value.  For patterns, that's actually the
        // *subpattern*, generally speaking.
        //
        // To see what I mean about ids etc, consider:
        //
        //     let x = @@3;
        //     alt x {
        //       @@y { ... }
        //     }
        //
        // Here the cmt for `y` would be something like
        //
        //     local(x)->@->@
        //
        // where the id of `local(x)` is the id of the `x` that appears
        // in the alt, the id of `local(x)->@` is the `@y` pattern,
        // and the id of `local(x)->@->@` is the id of the `y` pattern.

        #debug["gather_pat: id=%d pat=%s cmt=%s arm_id=%d alt_id=%d",
               pat.id, pprust::pat_to_str(pat),
               self.bccx.cmt_to_repr(cmt), arm_id, alt_id];
        let _i = indenter();

        let tcx = self.tcx();
        alt pat.node {
          ast::pat_wild {
            // _
          }

          ast::pat_enum(_, none) {
            // variant(*)
          }
          ast::pat_enum(_, some(subpats)) {
            // variant(x, y, z)
            for subpats.each { |subpat|
                let subcmt = self.bccx.cat_variant(subpat, cmt);
                self.gather_pat(subcmt, subpat, arm_id, alt_id);
            }
          }

          ast::pat_ident(_, none) if self.pat_is_variant(pat) {
            // nullary variant
            #debug["nullary variant"];
          }
          ast::pat_ident(id, o_pat) {
            // x or x @ p --- `x` must remain valid for the scope of the alt
            #debug["defines identifier %s", pprust::path_to_str(id)];

            // Note: there is a discussion of the function of
            // cat_discr in the method preserve():
            let cmt1 = self.bccx.cat_discr(cmt, alt_id);
            let arm_scope = ty::re_scope(arm_id);
            self.guarantee_valid(cmt1, m_const, arm_scope);

            for o_pat.each { |p|
                self.gather_pat(cmt, p, arm_id, alt_id);
            }
          }

          ast::pat_rec(field_pats, _) {
            // {f1: p1, ..., fN: pN}
            for field_pats.each { |fp|
                let cmt_field = self.bccx.cat_field(fp.pat, cmt, fp.ident);
                self.gather_pat(cmt_field, fp.pat, arm_id, alt_id);
            }
          }

          ast::pat_tup(subpats) {
            // (p1, ..., pN)
            for subpats.each { |subpat|
                let subcmt = self.bccx.cat_tuple_elt(subpat, cmt);
                self.gather_pat(subcmt, subpat, arm_id, alt_id);
            }
          }

          ast::pat_box(subpat) | ast::pat_uniq(subpat) {
            // @p1, ~p1
            alt self.bccx.cat_deref(subpat, cmt, 0u, true) {
              some(subcmt) {
                self.gather_pat(subcmt, subpat, arm_id, alt_id);
              }
              none {
                tcx.sess.span_bug(pat.span, "Non derefable type");
              }
            }
          }

          ast::pat_lit(_) | ast::pat_range(_, _) { /*always ok*/ }
        }
    }

    fn pat_is_variant(pat: @ast::pat) -> bool {
        pat_util::pat_is_variant(self.bccx.tcx.def_map, pat)
    }
}

<commit_msg>exempt unsafe ptrs from by-val checks<commit_after>// ----------------------------------------------------------------------
// Gathering loans
//
// The borrow check proceeds in two phases. In phase one, we gather the full
// set of loans that are required at any point.  These are sorted according to
// their associated scopes.  In phase two, checking loans, we will then make
// sure that all of these loans are honored.

import categorization::{public_methods, opt_deref_kind};
import loan::public_methods;
import preserve::public_methods;

export gather_loans;

enum gather_loan_ctxt = @{bccx: borrowck_ctxt, req_maps: req_maps};

fn gather_loans(bccx: borrowck_ctxt, crate: @ast::crate) -> req_maps {
    let glcx = gather_loan_ctxt(@{bccx: bccx,
                                  req_maps: {req_loan_map: int_hash(),
                                             pure_map: int_hash()}});
    let v = visit::mk_vt(@{visit_expr: req_loans_in_expr
                           with *visit::default_visitor()});
    visit::visit_crate(*crate, glcx, v);
    ret glcx.req_maps;
}

fn req_loans_in_expr(ex: @ast::expr,
                     &&self: gather_loan_ctxt,
                     vt: visit::vt<gather_loan_ctxt>) {
    let bccx = self.bccx;
    let tcx = bccx.tcx;

    #debug["req_loans_in_expr(ex=%s)", pprust::expr_to_str(ex)];

    // If this expression is borrowed, have to ensure it remains valid:
    for tcx.borrowings.find(ex.id).each { |borrow|
        let cmt = self.bccx.cat_borrow_of_expr(ex);
        let scope_r = ty::re_scope(borrow.scope_id);
        self.guarantee_valid(cmt, borrow.mutbl, scope_r);
    }

    // Special checks for various kinds of expressions:
    alt ex.node {
      ast::expr_addr_of(mutbl, base) {
        let base_cmt = self.bccx.cat_expr(base);

        // make sure that the thing we are pointing out stays valid
        // for the lifetime `scope_r` of the resulting ptr:
        let scope_r =
            alt check ty::get(tcx.ty(ex)).struct {
              ty::ty_rptr(r, _) { r }
            };
        self.guarantee_valid(base_cmt, mutbl, scope_r);
      }

      ast::expr_call(f, args, _) {
        let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
        let scope_r = ty::re_scope(ex.id);
        vec::iter2(args, arg_tys) { |arg, arg_ty|
            alt ty::resolved_mode(self.tcx(), arg_ty.mode) {
              ast::by_mutbl_ref {
                let arg_cmt = self.bccx.cat_expr(arg);
                self.guarantee_valid(arg_cmt, m_mutbl, scope_r);
              }
              ast::by_ref {
                let arg_cmt = self.bccx.cat_expr(arg);
                self.guarantee_valid(arg_cmt, m_imm,  scope_r);
              }
              ast::by_val {
                // Rust's by-val does not actually give ownership to
                // the callee.  This means that if a pointer type is
                // passed, it is effectively a borrow, and so the
                // caller must guarantee that the data remains valid.
                //
                // Subtle: we only guarantee that the pointer is valid
                // and const.  Technically, we ought to pass in the
                // mutability that the caller expects (e.g., if the
                // formal argument has type @mut, we should guarantee
                // validity and mutability, not validity and const).
                // However, the type system already guarantees that
                // the caller's mutability is compatible with the
                // callee, so this is not necessary.  (Note that with
                // actual borrows, typeck is more liberal and allows
                // the pointer to be borrowed as immutable even if it
                // is mutable in the caller's frame, thus effectively
                // passing the buck onto us to enforce this)

                alt opt_deref_kind(arg_ty.ty) {
                  some(deref_ptr(region_ptr)) |
                  some(deref_ptr(unsafe_ptr)) {
                    /* region pointers are (by induction) guaranteed */
                    /* unsafe pointers are the user's problem */
                  }
                  none {
                    /* not a pointer, no worries */
                  }
                  some(_) {
                    let arg_cmt = self.bccx.cat_borrow_of_expr(arg);
                    self.guarantee_valid(arg_cmt, m_const, scope_r);
                  }
                }
              }
              ast::by_move | ast::by_copy {}
            }
        }
      }

      ast::expr_alt(ex_v, arms, _) {
        let cmt = self.bccx.cat_expr(ex_v);
        for arms.each { |arm|
            for arm.pats.each { |pat|
                self.gather_pat(cmt, pat, arm.body.node.id, ex.id);
            }
        }
      }

      ast::expr_index(rcvr, _) |
      ast::expr_binary(_, rcvr, _) |
      ast::expr_unary(_, rcvr) if self.bccx.method_map.contains_key(ex.id) {
        // Receivers in method calls are always passed by ref.
        //
        // Here, in an overloaded operator, the call is this expression,
        // and hence the scope of the borrow is this call.
        //
        // FIXME/NOT REALLY---technically we should check the other
        // argument and consider the argument mode.  But how annoying.
        // And this problem when goes away when argument modes are
        // phased out.  So I elect to leave this undone.
        let scope_r = ty::re_scope(ex.id);
        let rcvr_cmt = self.bccx.cat_expr(rcvr);
        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
      }

      ast::expr_field(rcvr, _, _)
      if self.bccx.method_map.contains_key(ex.id) {
        // Receivers in method calls are always passed by ref.
        //
        // Here, the field a.b is in fact a closure.  Eventually, this
        // should be an fn&, but for now it's an fn@.  In any case,
        // the enclosing scope is either the call where it is a rcvr
        // (if used like `a.b(...)`), the call where it's an argument
        // (if used like `x(a.b)`), or the block (if used like `let x
        // = a.b`).
        let scope_r = ty::re_scope(self.tcx().region_map.get(ex.id));
        let rcvr_cmt = self.bccx.cat_expr(rcvr);
        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
      }

      _ { /*ok*/ }
    }

    // Check any contained expressions:
    visit::visit_expr(ex, self, vt);
}

impl methods for gather_loan_ctxt {
    fn tcx() -> ty::ctxt { self.bccx.tcx }

    // guarantees that addr_of(cmt) will be valid for the duration of
    // `static_scope_r`, or reports an error.  This may entail taking
    // out loans, which will be added to the `req_loan_map`.  This can
    // also entail "rooting" GC'd pointers, which means ensuring
    // dynamically that they are not freed.
    fn guarantee_valid(cmt: cmt,
                       req_mutbl: ast::mutability,
                       scope_r: ty::region) {

        #debug["guarantee_valid(cmt=%s, req_mutbl=%s, scope_r=%s)",
               self.bccx.cmt_to_repr(cmt),
               self.bccx.mut_to_str(req_mutbl),
               region_to_str(self.tcx(), scope_r)];
        let _i = indenter();

        alt cmt.lp {
          // If this expression is a loanable path, we MUST take out a loan.
          // This is somewhat non-obvious.  You might think, for example, that
          // if we have an immutable local variable `x` whose value is being
          // borrowed, we could rely on `x` not to change.  This is not so,
          // however, because even immutable locals can be moved.  So we take
          // out a loan on `x`, guaranteeing that it remains immutable for the
          // duration of the reference: if there is an attempt to move it
          // within that scope, the loan will be detected and an error will be
          // reported.
          some(_) {
            alt scope_r {
              ty::re_scope(scope_id) {
                let loans = self.bccx.loan(cmt, req_mutbl);
                self.add_loans(scope_id, loans);
              }
              _ {
                self.bccx.span_err(
                    cmt.span,
                    #fmt["cannot guarantee the stability \
                          of this expression for the entirety of \
                          its lifetime, %s",
                         region_to_str(self.tcx(), scope_r)]);
              }
            }
          }

          // The path is not loanable: in that case, we must try and preserve
          // it dynamically (or see that it is preserved by virtue of being
          // rooted in some immutable path)
          none {
            let opt_scope_id = alt scope_r {
              ty::re_scope(scope_id) { some(scope_id) }
              _ { none }
            };

            let result = {
                self.check_mutbl(req_mutbl, cmt).chain { |_ok|
                    self.bccx.preserve(cmt, opt_scope_id)
                }
            };

            alt result {
              ok(()) {
                // we were able guarantee the validity of the ptr,
                // perhaps by rooting or because it is immutably
                // rooted.  good.
              }
              err(e) {
                // not able to guarantee the validity of the ptr.
                // rather than report an error, presuming that the
                // borrow is for a limited scope, we'll make one last
                // ditch effort and require that the scope where the
                // borrow occurs be pure.
                alt opt_scope_id {
                  some(scope_id) {
                    self.req_maps.pure_map.insert(scope_id, e);
                  }
                  none {
                    // otherwise, fine, I give up.
                    self.bccx.report(e);
                  }
                }
              }
            }
          }
        }
    }

    // Check that the pat `cmt` is compatible with the required
    // mutability, presuming that it can be preserved to stay alive
    // long enough.
    //
    // For example, if you have an expression like `&x.f` where `x`
    // has type `@mut{f:int}`, this check might fail because `&x.f`
    // reqires an immutable pointer, but `f` lives in (aliased)
    // mutable memory.
    fn check_mutbl(req_mutbl: ast::mutability,
                   cmt: cmt) -> bckres<()> {
        alt (req_mutbl, cmt.mutbl) {
          (m_const, _) |
          (m_imm, m_imm) |
          (m_mutbl, m_mutbl) {
            ok(())
          }

          (_, m_const) |
          (m_imm, m_mutbl) |
          (m_mutbl, m_imm) {
            err({cmt: cmt,
                 code: err_mutbl(req_mutbl, cmt.mutbl)})
          }
        }
    }

    fn add_loans(scope_id: ast::node_id, loans: @const [loan]) {
        alt self.req_maps.req_loan_map.find(scope_id) {
          some(l) {
            *l += [loans];
          }
          none {
            self.req_maps.req_loan_map.insert(scope_id, @mut [loans]);
          }
        }
    }

    fn gather_pat(cmt: cmt, pat: @ast::pat,
                  arm_id: ast::node_id, alt_id: ast::node_id) {

        // Here, `cmt` is the categorization for the value being
        // matched and pat is the pattern it is being matched against.
        //
        // In general, the way that this works is that we walk down
        // the pattern, constructing a cmt that represents the path
        // that will be taken to reach the value being matched.
        //
        // When we encounter named bindings, we take the cmt that has
        // been built up and pass it off to guarantee_valid() so that
        // we can be sure that the binding will remain valid for the
        // duration of the arm.
        //
        // The correspondence between the id in the cmt and which
        // pattern is being referred to is somewhat...subtle.  In
        // general, the id of the cmt is the id of the node that
        // produces the value.  For patterns, that's actually the
        // *subpattern*, generally speaking.
        //
        // To see what I mean about ids etc, consider:
        //
        //     let x = @@3;
        //     alt x {
        //       @@y { ... }
        //     }
        //
        // Here the cmt for `y` would be something like
        //
        //     local(x)->@->@
        //
        // where the id of `local(x)` is the id of the `x` that appears
        // in the alt, the id of `local(x)->@` is the `@y` pattern,
        // and the id of `local(x)->@->@` is the id of the `y` pattern.

        #debug["gather_pat: id=%d pat=%s cmt=%s arm_id=%d alt_id=%d",
               pat.id, pprust::pat_to_str(pat),
               self.bccx.cmt_to_repr(cmt), arm_id, alt_id];
        let _i = indenter();

        let tcx = self.tcx();
        alt pat.node {
          ast::pat_wild {
            // _
          }

          ast::pat_enum(_, none) {
            // variant(*)
          }
          ast::pat_enum(_, some(subpats)) {
            // variant(x, y, z)
            for subpats.each { |subpat|
                let subcmt = self.bccx.cat_variant(subpat, cmt);
                self.gather_pat(subcmt, subpat, arm_id, alt_id);
            }
          }

          ast::pat_ident(_, none) if self.pat_is_variant(pat) {
            // nullary variant
            #debug["nullary variant"];
          }
          ast::pat_ident(id, o_pat) {
            // x or x @ p --- `x` must remain valid for the scope of the alt
            #debug["defines identifier %s", pprust::path_to_str(id)];

            // Note: there is a discussion of the function of
            // cat_discr in the method preserve():
            let cmt1 = self.bccx.cat_discr(cmt, alt_id);
            let arm_scope = ty::re_scope(arm_id);
            self.guarantee_valid(cmt1, m_const, arm_scope);

            for o_pat.each { |p|
                self.gather_pat(cmt, p, arm_id, alt_id);
            }
          }

          ast::pat_rec(field_pats, _) {
            // {f1: p1, ..., fN: pN}
            for field_pats.each { |fp|
                let cmt_field = self.bccx.cat_field(fp.pat, cmt, fp.ident);
                self.gather_pat(cmt_field, fp.pat, arm_id, alt_id);
            }
          }

          ast::pat_tup(subpats) {
            // (p1, ..., pN)
            for subpats.each { |subpat|
                let subcmt = self.bccx.cat_tuple_elt(subpat, cmt);
                self.gather_pat(subcmt, subpat, arm_id, alt_id);
            }
          }

          ast::pat_box(subpat) | ast::pat_uniq(subpat) {
            // @p1, ~p1
            alt self.bccx.cat_deref(subpat, cmt, 0u, true) {
              some(subcmt) {
                self.gather_pat(subcmt, subpat, arm_id, alt_id);
              }
              none {
                tcx.sess.span_bug(pat.span, "Non derefable type");
              }
            }
          }

          ast::pat_lit(_) | ast::pat_range(_, _) { /*always ok*/ }
        }
    }

    fn pat_is_variant(pat: @ast::pat) -> bool {
        pat_util::pat_is_variant(self.bccx.tcx.def_map, pat)
    }
}

<|endoftext|>


<commit_before>//! Files, and methods and fields to access their metadata.

use std::fs;
use std::io::Error as IOError;
use std::io::Result as IOResult;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::{Path, PathBuf};

use fs::dir::Dir;
use fs::fields as f;

#[cfg(any(target_os = "macos", target_os = "linux"))]
use std::os::unix::fs::FileTypeExt;

#[allow(trivial_numeric_casts)]
mod modes {
    use libc;

    pub type Mode = u32;
    // The `libc::mode_t` type’s actual type varies, but the value returned
    // from `metadata.permissions().mode()` is always `u32`.

    pub const USER_READ: Mode     = libc::S_IRUSR as Mode;
    pub const USER_WRITE: Mode    = libc::S_IWUSR as Mode;
    pub const USER_EXECUTE: Mode  = libc::S_IXUSR as Mode;
    pub const GROUP_READ: Mode    = libc::S_IRGRP as Mode;
    pub const GROUP_WRITE: Mode   = libc::S_IWGRP as Mode;
    pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
    pub const OTHER_READ: Mode    = libc::S_IROTH as Mode;
    pub const OTHER_WRITE: Mode   = libc::S_IWOTH as Mode;
    pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
}

/// A **File** is a wrapper around one of Rust's Path objects, along with
/// associated data about the file.
///
/// Each file is definitely going to have its filename displayed at least
/// once, have its file extension extracted at least once, and have its metadata
/// information queried at least once, so it makes sense to do all this at the
/// start and hold on to all the information.
pub struct File<'dir> {

    /// The filename portion of this file's path, including the extension.
    ///
    /// This is used to compare against certain filenames (such as checking if
    /// it’s “Makefile” or something) and to highlight only the filename in
    /// colour when displaying the path.
    pub name: String,

    /// The file’s name’s extension, if present, extracted from the name.
    ///
    /// This is queried many times over, so it’s worth caching it.
    pub ext: Option<String>,

    /// The path that begat this file.
    ///
    /// Even though the file's name is extracted, the path needs to be kept
    /// around, as certain operations involve looking up the file's absolute
    /// location (such as the Git status, or searching for compiled files).
    pub path: PathBuf,

    /// A cached `metadata` call for this file.
    ///
    /// This too is queried multiple times, and is *not* cached by the OS, as
    /// it could easily change between invocations - but exa is so short-lived
    /// it's better to just cache it.
    pub metadata: fs::Metadata,

    /// A reference to the directory that contains this file, if present.
    ///
    /// Filenames that get passed in on the command-line directly will have no
    /// parent directory reference - although they technically have one on the
    /// filesystem, we'll never need to look at it, so it'll be `None`.
    /// However, *directories* that get passed in will produce files that
    /// contain a reference to it, which is used in certain operations (such
    /// as looking up a file's Git status).
    pub dir: Option<&'dir Dir>,
}

impl<'dir> File<'dir> {

    /// Create a new `File` object from the given `Path`, inside the given
    /// `Dir`, if appropriate.
    ///
    /// This uses `symlink_metadata` instead of `metadata`, which doesn't
    /// follow symbolic links.
    pub fn from_path(path: &Path, parent: Option<&'dir Dir>) -> IOResult<File<'dir>> {
        fs::symlink_metadata(path).map(|metadata| File::with_metadata(metadata, path, parent))
    }

    /// Create a new File object from the given metadata result, and other data.
    pub fn with_metadata(metadata: fs::Metadata, path: &Path, parent: Option<&'dir Dir>) -> File<'dir> {
        let filename = match path.components().next_back() {
            Some(comp) => comp.as_os_str().to_string_lossy().to_string(),
            None       => String::new(),
        };

        File {
            path:      path.to_path_buf(),
            dir:       parent,
            metadata:  metadata,
            ext:       ext(path),
            name:      filename,
        }
    }

    /// Whether this file is a directory on the filesystem.
    pub fn is_directory(&self) -> bool {
        self.metadata.is_dir()
    }

    /// If this file is a directory on the filesystem, then clone its
    /// `PathBuf` for use in one of our own `Dir` objects, and read a list of
    /// its contents.
    ///
    /// Returns an IO error upon failure, but this shouldn't be used to check
    /// if a `File` is a directory or not! For that, just use `is_directory()`.
    pub fn to_dir(&self, scan_for_git: bool) -> IOResult<Dir> {
        Dir::read_dir(&*self.path, scan_for_git)
    }

    /// Whether this file is a regular file on the filesystem - that is, not a
    /// directory, a link, or anything else treated specially.
    pub fn is_file(&self) -> bool {
        self.metadata.is_file()
    }

    /// Whether this file is both a regular file *and* executable for the
    /// current user. Executable files have different semantics than
    /// executable directories, and so should be highlighted differently.
    pub fn is_executable_file(&self) -> bool {
        let bit = modes::USER_EXECUTE;
        self.is_file() && (self.metadata.permissions().mode() & bit) == bit
    }

    /// Whether this file is a symlink on the filesystem.
    pub fn is_link(&self) -> bool {
        self.metadata.file_type().is_symlink()
    }

    /// Whether this file is a dotfile, based on its name. In Unix, file names
    /// beginning with a dot represent system or configuration files, and
    /// should be hidden by default.
    pub fn is_dotfile(&self) -> bool {
        self.name.starts_with('.')
    }

    /// Assuming the current file is a symlink, follows the link and
    /// returns a File object from the path the link points to.
    ///
    /// If statting the file fails (usually because the file on the
    /// other end doesn't exist), returns the path to the file
    /// that should be there.
    pub fn link_target(&self) -> FileTarget<'dir> {
        let path = match fs::read_link(&self.path) {
            Ok(path)  => path,
            Err(e)    => return FileTarget::Err(e),
        };

        let (metadata, ext) = {
            let target_path_ = match self.dir {
                Some(dir) if dir.path != Path::new(".") => Some(dir.join(&*path)),
                _                                       => None
            };
            let target_path = target_path_.as_ref().unwrap_or(&path);
            // Use plain `metadata` instead of `symlink_metadata` - we *want* to follow links.
            (fs::metadata(&target_path), ext(&target_path))
        };

        let filename = match path.components().next_back() {
            Some(comp) => comp.as_os_str().to_string_lossy().to_string(),
            None       => String::new(),
        };

        if let Ok(metadata) = metadata {
            FileTarget::Ok(File {
                path:      path,
                dir:       self.dir,
                metadata:  metadata,
                ext:       ext,
                name:      filename,
            })
        }
        else {
            FileTarget::Broken(path)
        }
    }

    /// This file's number of hard links.
    ///
    /// It also reports whether this is both a regular file, and a file with
    /// multiple links. This is important, because a file with multiple links
    /// is uncommon, while you can come across directories and other types
    /// with multiple links much more often. Thus, it should get highlighted
    /// more attentively.
    pub fn links(&self) -> f::Links {
        let count = self.metadata.nlink();

        f::Links {
            count: count,
            multiple: self.is_file() && count > 1,
        }
    }

    /// This file's inode.
    pub fn inode(&self) -> f::Inode {
        f::Inode(self.metadata.ino())
    }

    /// This file's number of filesystem blocks.
    ///
    /// (Not the size of each block, which we don't actually report on)
    pub fn blocks(&self) -> f::Blocks {
        if self.is_file() || self.is_link() {
            f::Blocks::Some(self.metadata.blocks())
        }
        else {
            f::Blocks::None
        }
    }

    /// The ID of the user that own this file.
    pub fn user(&self) -> f::User {
        f::User(self.metadata.uid())
    }

    /// The ID of the group that owns this file.
    pub fn group(&self) -> f::Group {
        f::Group(self.metadata.gid())
    }

    /// This file's size, if it's a regular file.
    ///
    /// For directories, no size is given. Although they do have a size on
    /// some filesystems, I've never looked at one of those numbers and gained
    /// any information from it. So it's going to be hidden instead.
    pub fn size(&self) -> f::Size {
        if self.is_directory() {
            f::Size::None
        }
        else {
            f::Size::Some(self.metadata.len())
        }
    }

    pub fn modified_time(&self) -> f::Time {
        f::Time(self.metadata.mtime())
    }

    pub fn created_time(&self) -> f::Time {
        f::Time(self.metadata.ctime())
    }

    pub fn accessed_time(&self) -> f::Time {
        f::Time(self.metadata.mtime())
    }

    /// This file's 'type'.
    ///
    /// This is used in the leftmost column of the permissions column.
    /// Although the file type can usually be guessed from the colour of the
    /// file, `ls` puts this character there, so people will expect it.
    pub fn type_char(&self) -> f::Type {
        if self.is_file() {
            f::Type::File
        }
        else if self.is_directory() {
            f::Type::Directory
        }
        else if self.is_pipe() {
            f::Type::Pipe
        }
        else if self.is_link() {
            f::Type::Link
        }
        else if self.is_char_device() {
            f::Type::CharDevice
        }
        else if self.is_block_device() {
            f::Type::BlockDevice
        }
        else if self.is_socket() {
            f::Type::Socket
        }
        else {
            f::Type::Special
        }
    }

    /// This file's permissions, with flags for each bit.
    ///
    /// The extended-attribute '@' character that you see in here is in fact
    /// added in later, to avoid querying the extended attributes more than
    /// once. (Yes, it's a little hacky.)
    pub fn permissions(&self) -> f::Permissions {
        let bits = self.metadata.permissions().mode();
        let has_bit = |bit| { bits & bit == bit };

        f::Permissions {
            user_read:      has_bit(modes::USER_READ),
            user_write:     has_bit(modes::USER_WRITE),
            user_execute:   has_bit(modes::USER_EXECUTE),
            group_read:     has_bit(modes::GROUP_READ),
            group_write:    has_bit(modes::GROUP_WRITE),
            group_execute:  has_bit(modes::GROUP_EXECUTE),
            other_read:     has_bit(modes::OTHER_READ),
            other_write:    has_bit(modes::OTHER_WRITE),
            other_execute:  has_bit(modes::OTHER_EXECUTE),
        }
    }

    /// Whether this file's extension is any of the strings that get passed in.
    ///
    /// This will always return `false` if the file has no extension.
    pub fn extension_is_one_of(&self, choices: &[&str]) -> bool {
        match self.ext {
            Some(ref ext)  => choices.contains(&&ext[..]),
            None           => false,
        }
    }

    /// Whether this file's name, including extension, is any of the strings
    /// that get passed in.
    pub fn name_is_one_of(&self, choices: &[&str]) -> bool {
        choices.contains(&&self.name[..])
    }

    /// This file's Git status as two flags: one for staged changes, and the
    /// other for unstaged changes.
    ///
    /// This requires looking at the `git` field of this file's parent
    /// directory, so will not work if this file has just been passed in on
    /// the command line.
    pub fn git_status(&self) -> f::Git {
        use std::env::current_dir;

        match self.dir {
            None    => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified },
            Some(d) => {
                let cwd = match current_dir() {
                    Err(_)  => Path::new(".").join(&self.path),
                    Ok(dir) => dir.join(&self.path),
                };

                d.git_status(&cwd, self.is_directory())
            },
        }
    }
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl<'dir> File<'dir> {
    /// Whether this file is a named pipe on the filesystem.
    pub fn is_pipe(&self) -> bool {
        self.metadata.file_type().is_fifo()
    }

    /// Whether this file is a char device on the filesystem.
    pub fn is_char_device(&self) -> bool {
        self.metadata.file_type().is_char_device()
    }

    /// Whether this file is a block device on the filesystem.
    pub fn is_block_device(&self) -> bool {
        self.metadata.file_type().is_block_device()
    }

    /// Whether this file is a socket on the filesystem.
    pub fn is_socket(&self) -> bool {
        self.metadata.file_type().is_socket()
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl<'dir> File<'dir> {
    /// Whether this file is a named pipe on the filesystem.
    pub fn is_pipe(&self) -> bool {
        false
    }

    /// Whether this file is a char device on the filesystem.
    pub fn is_char_device(&self) -> bool {
        false
    }

    /// Whether this file is a block device on the filesystem.
    pub fn is_block_device(&self) -> bool {
        false
    }

    /// Whether this file is a socket on the filesystem.
    pub fn is_socket(&self) -> bool {
        false
    }
}

impl<'a> AsRef<File<'a>> for File<'a> {
    fn as_ref(&self) -> &File<'a> {
        self
    }
}

/// Extract an extension from a file path, if one is present, in lowercase.
///
/// The extension is the series of characters after the last dot. This
/// deliberately counts dotfiles, so the ".git" folder has the extension "git".
///
/// ASCII lowercasing is used because these extensions are only compared
/// against a pre-compiled list of extensions which are known to only exist
/// within ASCII, so it's alright.
fn ext(path: &Path) -> Option<String> {
    use std::ascii::AsciiExt;

    let name = match path.file_name() {
        Some(f) => f.to_string_lossy().to_string(),
        None => return None,
    };

    name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase())
}

/// The result of following a symlink.
pub enum FileTarget<'dir> {

    /// The symlink pointed at a file that exists.
    Ok(File<'dir>),

    /// The symlink pointed at a file that does not exist. Holds the path
    /// where the file would be, if it existed.
    Broken(PathBuf),

    /// There was an IO error when following the link. This can happen if the
    /// file isn't a link to begin with, but also if, say, we don't have
    /// permission to follow it.
    Err(IOError),
}

#[cfg(test)]
mod test {
    use super::ext;
    use std::path::Path;

    #[test]
    fn extension() {
        assert_eq!(Some("dat".to_string()), ext(Path::new("fester.dat")))
    }

    #[test]
    fn dotfile() {
        assert_eq!(Some("vimrc".to_string()), ext(Path::new(".vimrc")))
    }

    #[test]
    fn no_extension() {
        assert_eq!(None, ext(Path::new("jarlsberg")))
    }
}
<commit_msg>Make FileTypeExt non-optional<commit_after>//! Files, and methods and fields to access their metadata.

use std::fs;
use std::io::Error as IOError;
use std::io::Result as IOResult;
use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt};
use std::path::{Path, PathBuf};

use fs::dir::Dir;
use fs::fields as f;

#[allow(trivial_numeric_casts)]
mod modes {
    use libc;

    pub type Mode = u32;
    // The `libc::mode_t` type’s actual type varies, but the value returned
    // from `metadata.permissions().mode()` is always `u32`.

    pub const USER_READ: Mode     = libc::S_IRUSR as Mode;
    pub const USER_WRITE: Mode    = libc::S_IWUSR as Mode;
    pub const USER_EXECUTE: Mode  = libc::S_IXUSR as Mode;
    pub const GROUP_READ: Mode    = libc::S_IRGRP as Mode;
    pub const GROUP_WRITE: Mode   = libc::S_IWGRP as Mode;
    pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
    pub const OTHER_READ: Mode    = libc::S_IROTH as Mode;
    pub const OTHER_WRITE: Mode   = libc::S_IWOTH as Mode;
    pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
}

/// A **File** is a wrapper around one of Rust's Path objects, along with
/// associated data about the file.
///
/// Each file is definitely going to have its filename displayed at least
/// once, have its file extension extracted at least once, and have its metadata
/// information queried at least once, so it makes sense to do all this at the
/// start and hold on to all the information.
pub struct File<'dir> {

    /// The filename portion of this file's path, including the extension.
    ///
    /// This is used to compare against certain filenames (such as checking if
    /// it’s “Makefile” or something) and to highlight only the filename in
    /// colour when displaying the path.
    pub name: String,

    /// The file’s name’s extension, if present, extracted from the name.
    ///
    /// This is queried many times over, so it’s worth caching it.
    pub ext: Option<String>,

    /// The path that begat this file.
    ///
    /// Even though the file's name is extracted, the path needs to be kept
    /// around, as certain operations involve looking up the file's absolute
    /// location (such as the Git status, or searching for compiled files).
    pub path: PathBuf,

    /// A cached `metadata` call for this file.
    ///
    /// This too is queried multiple times, and is *not* cached by the OS, as
    /// it could easily change between invocations - but exa is so short-lived
    /// it's better to just cache it.
    pub metadata: fs::Metadata,

    /// A reference to the directory that contains this file, if present.
    ///
    /// Filenames that get passed in on the command-line directly will have no
    /// parent directory reference - although they technically have one on the
    /// filesystem, we'll never need to look at it, so it'll be `None`.
    /// However, *directories* that get passed in will produce files that
    /// contain a reference to it, which is used in certain operations (such
    /// as looking up a file's Git status).
    pub dir: Option<&'dir Dir>,
}

impl<'dir> File<'dir> {

    /// Create a new `File` object from the given `Path`, inside the given
    /// `Dir`, if appropriate.
    ///
    /// This uses `symlink_metadata` instead of `metadata`, which doesn't
    /// follow symbolic links.
    pub fn from_path(path: &Path, parent: Option<&'dir Dir>) -> IOResult<File<'dir>> {
        fs::symlink_metadata(path).map(|metadata| File::with_metadata(metadata, path, parent))
    }

    /// Create a new File object from the given metadata result, and other data.
    pub fn with_metadata(metadata: fs::Metadata, path: &Path, parent: Option<&'dir Dir>) -> File<'dir> {
        let filename = match path.components().next_back() {
            Some(comp) => comp.as_os_str().to_string_lossy().to_string(),
            None       => String::new(),
        };

        File {
            path:      path.to_path_buf(),
            dir:       parent,
            metadata:  metadata,
            ext:       ext(path),
            name:      filename,
        }
    }

    /// Whether this file is a directory on the filesystem.
    pub fn is_directory(&self) -> bool {
        self.metadata.is_dir()
    }

    /// If this file is a directory on the filesystem, then clone its
    /// `PathBuf` for use in one of our own `Dir` objects, and read a list of
    /// its contents.
    ///
    /// Returns an IO error upon failure, but this shouldn't be used to check
    /// if a `File` is a directory or not! For that, just use `is_directory()`.
    pub fn to_dir(&self, scan_for_git: bool) -> IOResult<Dir> {
        Dir::read_dir(&*self.path, scan_for_git)
    }

    /// Whether this file is a regular file on the filesystem - that is, not a
    /// directory, a link, or anything else treated specially.
    pub fn is_file(&self) -> bool {
        self.metadata.is_file()
    }

    /// Whether this file is both a regular file *and* executable for the
    /// current user. Executable files have different semantics than
    /// executable directories, and so should be highlighted differently.
    pub fn is_executable_file(&self) -> bool {
        let bit = modes::USER_EXECUTE;
        self.is_file() && (self.metadata.permissions().mode() & bit) == bit
    }

    /// Whether this file is a symlink on the filesystem.
    pub fn is_link(&self) -> bool {
        self.metadata.file_type().is_symlink()
    }

    /// Whether this file is a named pipe on the filesystem.
    pub fn is_pipe(&self) -> bool {
       self.metadata.file_type().is_fifo()
   }

   /// Whether this file is a char device on the filesystem.
   pub fn is_char_device(&self) -> bool {
       self.metadata.file_type().is_char_device()
   }

   /// Whether this file is a block device on the filesystem.
   pub fn is_block_device(&self) -> bool {
       self.metadata.file_type().is_block_device()
   }

   /// Whether this file is a socket on the filesystem.
   pub fn is_socket(&self) -> bool {
       self.metadata.file_type().is_socket()
   }

    /// Whether this file is a dotfile, based on its name. In Unix, file names
    /// beginning with a dot represent system or configuration files, and
    /// should be hidden by default.
    pub fn is_dotfile(&self) -> bool {
        self.name.starts_with('.')
    }

    /// Assuming the current file is a symlink, follows the link and
    /// returns a File object from the path the link points to.
    ///
    /// If statting the file fails (usually because the file on the
    /// other end doesn't exist), returns the path to the file
    /// that should be there.
    pub fn link_target(&self) -> FileTarget<'dir> {
        let path = match fs::read_link(&self.path) {
            Ok(path)  => path,
            Err(e)    => return FileTarget::Err(e),
        };

        let (metadata, ext) = {
            let target_path_ = match self.dir {
                Some(dir) if dir.path != Path::new(".") => Some(dir.join(&*path)),
                _                                       => None
            };
            let target_path = target_path_.as_ref().unwrap_or(&path);
            // Use plain `metadata` instead of `symlink_metadata` - we *want* to follow links.
            (fs::metadata(&target_path), ext(&target_path))
        };

        let filename = match path.components().next_back() {
            Some(comp) => comp.as_os_str().to_string_lossy().to_string(),
            None       => String::new(),
        };

        if let Ok(metadata) = metadata {
            FileTarget::Ok(File {
                path:      path,
                dir:       self.dir,
                metadata:  metadata,
                ext:       ext,
                name:      filename,
            })
        }
        else {
            FileTarget::Broken(path)
        }
    }

    /// This file's number of hard links.
    ///
    /// It also reports whether this is both a regular file, and a file with
    /// multiple links. This is important, because a file with multiple links
    /// is uncommon, while you can come across directories and other types
    /// with multiple links much more often. Thus, it should get highlighted
    /// more attentively.
    pub fn links(&self) -> f::Links {
        let count = self.metadata.nlink();

        f::Links {
            count: count,
            multiple: self.is_file() && count > 1,
        }
    }

    /// This file's inode.
    pub fn inode(&self) -> f::Inode {
        f::Inode(self.metadata.ino())
    }

    /// This file's number of filesystem blocks.
    ///
    /// (Not the size of each block, which we don't actually report on)
    pub fn blocks(&self) -> f::Blocks {
        if self.is_file() || self.is_link() {
            f::Blocks::Some(self.metadata.blocks())
        }
        else {
            f::Blocks::None
        }
    }

    /// The ID of the user that own this file.
    pub fn user(&self) -> f::User {
        f::User(self.metadata.uid())
    }

    /// The ID of the group that owns this file.
    pub fn group(&self) -> f::Group {
        f::Group(self.metadata.gid())
    }

    /// This file's size, if it's a regular file.
    ///
    /// For directories, no size is given. Although they do have a size on
    /// some filesystems, I've never looked at one of those numbers and gained
    /// any information from it. So it's going to be hidden instead.
    pub fn size(&self) -> f::Size {
        if self.is_directory() {
            f::Size::None
        }
        else {
            f::Size::Some(self.metadata.len())
        }
    }

    pub fn modified_time(&self) -> f::Time {
        f::Time(self.metadata.mtime())
    }

    pub fn created_time(&self) -> f::Time {
        f::Time(self.metadata.ctime())
    }

    pub fn accessed_time(&self) -> f::Time {
        f::Time(self.metadata.mtime())
    }

    /// This file's 'type'.
    ///
    /// This is used in the leftmost column of the permissions column.
    /// Although the file type can usually be guessed from the colour of the
    /// file, `ls` puts this character there, so people will expect it.
    pub fn type_char(&self) -> f::Type {
        if self.is_file() {
            f::Type::File
        }
        else if self.is_directory() {
            f::Type::Directory
        }
        else if self.is_pipe() {
            f::Type::Pipe
        }
        else if self.is_link() {
            f::Type::Link
        }
        else if self.is_char_device() {
            f::Type::CharDevice
        }
        else if self.is_block_device() {
            f::Type::BlockDevice
        }
        else if self.is_socket() {
            f::Type::Socket
        }
        else {
            f::Type::Special
        }
    }

    /// This file's permissions, with flags for each bit.
    ///
    /// The extended-attribute '@' character that you see in here is in fact
    /// added in later, to avoid querying the extended attributes more than
    /// once. (Yes, it's a little hacky.)
    pub fn permissions(&self) -> f::Permissions {
        let bits = self.metadata.permissions().mode();
        let has_bit = |bit| { bits & bit == bit };

        f::Permissions {
            user_read:      has_bit(modes::USER_READ),
            user_write:     has_bit(modes::USER_WRITE),
            user_execute:   has_bit(modes::USER_EXECUTE),
            group_read:     has_bit(modes::GROUP_READ),
            group_write:    has_bit(modes::GROUP_WRITE),
            group_execute:  has_bit(modes::GROUP_EXECUTE),
            other_read:     has_bit(modes::OTHER_READ),
            other_write:    has_bit(modes::OTHER_WRITE),
            other_execute:  has_bit(modes::OTHER_EXECUTE),
        }
    }

    /// Whether this file's extension is any of the strings that get passed in.
    ///
    /// This will always return `false` if the file has no extension.
    pub fn extension_is_one_of(&self, choices: &[&str]) -> bool {
        match self.ext {
            Some(ref ext)  => choices.contains(&&ext[..]),
            None           => false,
        }
    }

    /// Whether this file's name, including extension, is any of the strings
    /// that get passed in.
    pub fn name_is_one_of(&self, choices: &[&str]) -> bool {
        choices.contains(&&self.name[..])
    }

    /// This file's Git status as two flags: one for staged changes, and the
    /// other for unstaged changes.
    ///
    /// This requires looking at the `git` field of this file's parent
    /// directory, so will not work if this file has just been passed in on
    /// the command line.
    pub fn git_status(&self) -> f::Git {
        use std::env::current_dir;

        match self.dir {
            None    => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified },
            Some(d) => {
                let cwd = match current_dir() {
                    Err(_)  => Path::new(".").join(&self.path),
                    Ok(dir) => dir.join(&self.path),
                };

                d.git_status(&cwd, self.is_directory())
            },
        }
    }
}

impl<'a> AsRef<File<'a>> for File<'a> {
    fn as_ref(&self) -> &File<'a> {
        self
    }
}

/// Extract an extension from a file path, if one is present, in lowercase.
///
/// The extension is the series of characters after the last dot. This
/// deliberately counts dotfiles, so the ".git" folder has the extension "git".
///
/// ASCII lowercasing is used because these extensions are only compared
/// against a pre-compiled list of extensions which are known to only exist
/// within ASCII, so it's alright.
fn ext(path: &Path) -> Option<String> {
    use std::ascii::AsciiExt;

    let name = match path.file_name() {
        Some(f) => f.to_string_lossy().to_string(),
        None => return None,
    };

    name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase())
}

/// The result of following a symlink.
pub enum FileTarget<'dir> {

    /// The symlink pointed at a file that exists.
    Ok(File<'dir>),

    /// The symlink pointed at a file that does not exist. Holds the path
    /// where the file would be, if it existed.
    Broken(PathBuf),

    /// There was an IO error when following the link. This can happen if the
    /// file isn't a link to begin with, but also if, say, we don't have
    /// permission to follow it.
    Err(IOError),
}

#[cfg(test)]
mod test {
    use super::ext;
    use std::path::Path;

    #[test]
    fn extension() {
        assert_eq!(Some("dat".to_string()), ext(Path::new("fester.dat")))
    }

    #[test]
    fn dotfile() {
        assert_eq!(Some("vimrc".to_string()), ext(Path::new(".vimrc")))
    }

    #[test]
    fn no_extension() {
        assert_eq!(None, ext(Path::new("jarlsberg")))
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate transproblem;

use getopts::Options;
use transproblem::Transportation;
use transproblem::Error;
use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::PathBuf;

fn file_input(file: &PathBuf) -> Result<(Vec<u64>, Vec<u64>, Vec<Vec<u64>>), Box<std::error::Error>> {
    let f = BufReader::new(try!(File::open(file)));
    let mut lines: Vec<String> = try!(f.lines().collect());
    lines = lines.into_iter()
                 .filter(|line| {
                     line.trim();
                     !line.is_empty()
                 })
                 .collect();

    let a: Vec<u64> = try!(lines.iter()
                                .nth(0)
                                .unwrap_or(&"".to_owned())
                                .split_whitespace()
                                .map(|number| number.parse())
                                .collect());

    let b: Vec<u64> = try!(lines.iter()
                                .nth(1)
                                .unwrap_or(&"".to_owned())
                                .split_whitespace()
                                .map(|number| number.parse())
                                .collect());

    let c: Vec<Vec<u64>> = try!(lines.iter()
                                     .skip(2)
                                     .map(|line| {
                                         line.split_whitespace()
                                             .map(|number| number.parse())
                                             .collect()
                                     })
                                     .collect());

    Ok((a, b, c))
}

fn console_input() -> Result<(Vec<u64>, Vec<u64>, Vec<Vec<u64>>), io::Error> {
    let mut a: Vec<u64>;
    let mut b: Vec<u64>;
    let mut c: Vec<Vec<u64>>;
    let mut buffer = String::new();

    println!("Введите количество поставщиков");
    loop {
        print!("A = ");
        try!(io::stdout().flush());
        buffer.clear();
        try!(io::stdin().read_line(&mut buffer));
        match buffer.trim().parse() {
            Ok(size) => {
                if size != 0 {
                    a = vec![0;size];
                    break;
                } else {
                    println!("Ошибка: количество поставщиков должно быть больше 0");
                }
            }
            Err(_) => println!("Ошибка: количество поставщиков должно быть целым неотрицательным числом"),
        }
    }

    println!("Введите количество потребителей");
    loop {
        print!("B = ");
        try!(io::stdout().flush());
        buffer.clear();
        try!(io::stdin().read_line(&mut buffer));
        match buffer.trim().parse() {
            Ok(size) => {
                if size != 0 {
                    b = vec![0;size];
                    break;
                } else {
                    println!("Ошибка: количество потребителей должно быть больше 0");
                }
            }
            Err(_) => println!("Ошибка: количество потребителей должно быть целым неотрицательным числом"),
        }
    }

    println!("Введите количество груза у каждого поставщика");
    for i in 0..a.len() {
        loop {
            print!("a[{}] = ", i + 1);
            try!(io::stdout().flush());
            buffer.clear();
            try!(io::stdin().read_line(&mut buffer));
            match buffer.trim().parse() {
                Ok(amount) => {
                    a[i] = amount;
                    break;
                }
                Err(_) => println!("Ошибка: количество груза должно быть целым неотрицательным числом"),
            }
        }
    }

    println!("Введите количество заказоного груза у каждого потребителя");
    for i in 0..b.len() {
        loop {
            print!("b[{}] = ", i + 1);
            try!(io::stdout().flush());
            buffer.clear();
            try!(io::stdin().read_line(&mut buffer));
            match buffer.trim().parse() {
                Ok(amount) => {
                    b[i] = amount;
                    break;
                }
                Err(_) => println!("Ошибка: количество груза должно быть целым неотрицательным числом"),
            }
        }
    }

    println!("Введите стоимости перевозок");
    c = vec![vec![0;b.len()];a.len()];
    for i in 0..a.len() {
        for j in 0..b.len() {
            loop {
                print!("c[{}][{}] = ", i + 1, j + 1);
                try!(io::stdout().flush());
                buffer.clear();
                try!(io::stdin().read_line(&mut buffer));
                match buffer.trim().parse() {
                    Ok(cost) => {
                        c[i][j] = cost;
                        break;
                    }
                    Err(_) => println!("Ошибка: стоимость должна быть целым неотрицательным числом"),
                }
            }
        }
    }

    Ok((a, b, c))
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            print_usage(&opts, &f.to_string());
            std::process::exit(1);
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
        std::process::exit(0);
    }

    if matches.free.is_empty() {
        let (a, b, c) = match console_input() {
            Ok((a, b, c)) => (a, b, c),
            Err(err) => panic!(err.to_string()),
        };
        match Transportation::new(a, b, c) {
            Ok(mut t) => {
                t.potential_method();
                t.printstd();
            }
            Err(err) => panic!(err.to_string()),
        };
    } else {
        for file in matches.free.iter().map(PathBuf::from) {
            let (a, b, c) = match file_input(&file) {
                Ok((a, b, c)) => (a, b, c),
                Err(err) => {
                    println!("{:?}:{}", file, err.to_string());
                    continue;
                }
            };
            match Transportation::new(a, b, c) {
                Ok(mut t) => {
                    println!("{:?}", file);
                    t.potential_method();
                    t.printstd();
                }
                Err(err) => {
                    match err {
                        Error::NumOfSupOrCust => {
                            println!("{:?}: количество поставщиков и потребителей не должно быть меньше 0",
                                     file)
                        }
                        Error::NumOfRows => {
                            println!("{:?}: количество поставщиков не равно количеству строк в матрице стоимостей",
                                     file)
                        }

                        Error::NumOfCols => {
                            println!("{:?}: количество потребителей не равно количеству столбцов в матрице стоимостей",
                                     file)
                        }

                    }
                }
            };
        }
    }
}
<commit_msg>use VecDeque instead Vec<commit_after>extern crate getopts;
extern crate transproblem;

use getopts::Options;
use transproblem::Transportation;
use transproblem::Error;
use std::collections::VecDeque;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::PathBuf;

fn file_input(file: &PathBuf) -> Result<(Vec<u64>, Vec<u64>, Vec<Vec<u64>>), Box<std::error::Error>> {
    let f = BufReader::new(try!(File::open(file)));
    let mut lines: VecDeque<String> = try!(f.lines().collect());
    lines = lines.into_iter()
                 .filter(|line| {
                     line.trim();
                     !line.is_empty()
                 })
                 .collect();

    let a: Vec<u64> = try!(lines.pop_front()
                                .unwrap_or("".to_owned())
                                .split_whitespace()
                                .map(str::parse)
                                .collect());

    let b: Vec<u64> = try!(lines.pop_front()
                                .unwrap_or("".to_owned())
                                .split_whitespace()
                                .map(str::parse)
                                .collect());

    let c: Vec<Vec<u64>> = try!(lines.iter()
                                     .map(|line| {
                                         line.split_whitespace()
                                             .map(|number| number.parse())
                                             .collect()
                                     })
                                     .collect());

    Ok((a, b, c))
}

fn console_input() -> Result<(Vec<u64>, Vec<u64>, Vec<Vec<u64>>), io::Error> {
    let mut a: Vec<u64>;
    let mut b: Vec<u64>;
    let mut c: Vec<Vec<u64>>;
    let mut buffer = String::new();

    println!("Введите количество поставщиков");
    loop {
        print!("A = ");
        try!(io::stdout().flush());
        buffer.clear();
        try!(io::stdin().read_line(&mut buffer));
        match buffer.trim().parse() {
            Ok(size) => {
                if size != 0 {
                    a = vec![0;size];
                    break;
                } else {
                    println!("Ошибка: количество поставщиков должно быть больше 0");
                }
            }
            Err(_) => println!("Ошибка: количество поставщиков должно быть целым неотрицательным числом"),
        }
    }

    println!("Введите количество потребителей");
    loop {
        print!("B = ");
        try!(io::stdout().flush());
        buffer.clear();
        try!(io::stdin().read_line(&mut buffer));
        match buffer.trim().parse() {
            Ok(size) => {
                if size != 0 {
                    b = vec![0;size];
                    break;
                } else {
                    println!("Ошибка: количество потребителей должно быть больше 0");
                }
            }
            Err(_) => println!("Ошибка: количество потребителей должно быть целым неотрицательным числом"),
        }
    }

    println!("Введите количество груза у каждого поставщика");
    for i in 0..a.len() {
        loop {
            print!("a[{}] = ", i + 1);
            try!(io::stdout().flush());
            buffer.clear();
            try!(io::stdin().read_line(&mut buffer));
            match buffer.trim().parse() {
                Ok(amount) => {
                    a[i] = amount;
                    break;
                }
                Err(_) => println!("Ошибка: количество груза должно быть целым неотрицательным числом"),
            }
        }
    }

    println!("Введите количество заказоного груза у каждого потребителя");
    for i in 0..b.len() {
        loop {
            print!("b[{}] = ", i + 1);
            try!(io::stdout().flush());
            buffer.clear();
            try!(io::stdin().read_line(&mut buffer));
            match buffer.trim().parse() {
                Ok(amount) => {
                    b[i] = amount;
                    break;
                }
                Err(_) => println!("Ошибка: количество груза должно быть целым неотрицательным числом"),
            }
        }
    }

    println!("Введите стоимости перевозок");
    c = vec![vec![0;b.len()];a.len()];
    for i in 0..a.len() {
        for j in 0..b.len() {
            loop {
                print!("c[{}][{}] = ", i + 1, j + 1);
                try!(io::stdout().flush());
                buffer.clear();
                try!(io::stdin().read_line(&mut buffer));
                match buffer.trim().parse() {
                    Ok(cost) => {
                        c[i][j] = cost;
                        break;
                    }
                    Err(_) => println!("Ошибка: стоимость должна быть целым неотрицательным числом"),
                }
            }
        }
    }

    Ok((a, b, c))
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>...",
                         reason,
                         std::env::args_os().next().unwrap().to_string_lossy());
    println!("{}", opts.usage(&reason));
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(std::env::args().skip(1)) {
        Ok(m) => m,
        Err(f) => {
            print_usage(&opts, &f.to_string());
            std::process::exit(1);
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
        std::process::exit(0);
    }

    if matches.free.is_empty() {
        let (a, b, c) = match console_input() {
            Ok((a, b, c)) => (a, b, c),
            Err(err) => panic!(err.to_string()),
        };
        match Transportation::new(a, b, c) {
            Ok(mut t) => {
                t.potential_method();
                t.printstd();
            }
            Err(err) => panic!(err.to_string()),
        };
    } else {
        for file in matches.free.iter().map(PathBuf::from) {
            let (a, b, c) = match file_input(&file) {
                Ok((a, b, c)) => (a, b, c),
                Err(err) => {
                    println!("{:?}:{}", file, err.to_string());
                    continue;
                }
            };
            match Transportation::new(a, b, c) {
                Ok(mut t) => {
                    println!("{:?}", file);
                    t.potential_method();
                    t.printstd();
                }
                Err(err) => {
                    match err {
                        Error::NumOfSupOrCust => {
                            println!("{:?}: количество поставщиков и потребителей не должно быть меньше 0",
                                     file)
                        }
                        Error::NumOfRows => {
                            println!("{:?}: количество поставщиков не равно количеству строк в матрице стоимостей",
                                     file)
                        }

                        Error::NumOfCols => {
                            println!("{:?}: количество потребителей не равно количеству столбцов в матрице стоимостей",
                                     file)
                        }

                    }
                }
            };
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! ~~~rust
//! extern crate gfx;
//!
//! #[start]
//! fn start(argc: int, argv: **u8) -> int {
//!     native::start(argc, argv, main)
//! }
//!
//! fn main() {
//!     // spawn render task
//!     let (renderer, mut device) = gfx::start(()).unwrap();
//!
//!     // spawn game task
//!     spawn(proc {
//!         let _ = renderer; // do stuff with renderer
//!         loop {}
//!     })
//!
//!     loop {
//!         device.update(); // update device
//!     }
//! }
//! ~~~
//!
//! ~~~
//!     Render Task        |           Main Platform Thread             |         User Task
//!                        |                                            |
//! +----------------+     |                      +----------------+    |
//! |                |<----- device::Reply -------|                |    |
//! | device::Client |     |                      | device::Server |    |
//! |                |------ device::Request ---->|                |    |
//! +----------------+     |                      +----------------+    |
//!                        |                                            |
//!                        |                                            |     +----------------+
//!                        |<------------- render::Request -------------------|                |
//!                        |                                            |     | render::Client |
//!                        |-------------- render::Reply -------------------->|                |
//!                        |                                            |     +----------------+
//!                        |                                            |
//! ~~~

#![crate_id = "github.com/bjz/gfx-rs#gfx:0.1"]
#![comment = "A lightweight graphics device manager for Rust"]
#![license = "ASL2"]
#![crate_type = "lib"]

#![feature(macro_rules, phase)]
#[phase(plugin, link)] extern crate log;
extern crate libc;
extern crate device;

// public re-exports
pub use render::{BufferHandle, MeshHandle, SurfaceHandle, TextureHandle, SamplerHandle, ProgramHandle, EnvirHandle};
pub use render::Renderer;
pub use MeshSlice = render::mesh::Slice;
pub use render::mesh::{VertexCount, ElementCount, VertexSlice, IndexSlice};
pub use Environment = render::envir::Storage;
pub use render::envir::{BlockVar, UniformVar, TextureVar};
pub use render::target::{ClearData, Plane, Frame, TextureLayer, TextureLevel};
pub use Device = device::Server;
pub use device::{Color, GraphicsContext, InitError};
pub use device::shade::{UniformValue, ValueI32, ValueF32, ValueI32Vec, ValueF32Vec, ValueF32Matrix};

mod render;
pub mod platform;

#[allow(visible_private_types)]
pub fn start<Api, P: GraphicsContext<Api>>(graphics_context: P, options: device::Options)
        -> Result<(Renderer, Device<P, device::Device>), InitError> {
    device::init(graphics_context, options).map(|(client, server)| {
        (Renderer::new(client), server)
    })
}
<commit_msg>Remove obsolete comments<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_id = "github.com/bjz/gfx-rs#gfx:0.1"]
#![comment = "A lightweight graphics device manager for Rust"]
#![license = "ASL2"]
#![crate_type = "lib"]

#![feature(macro_rules, phase)]
#[phase(plugin, link)] extern crate log;
extern crate libc;
extern crate device;

// public re-exports
pub use render::{BufferHandle, MeshHandle, SurfaceHandle, TextureHandle, SamplerHandle, ProgramHandle, EnvirHandle};
pub use render::Renderer;
pub use MeshSlice = render::mesh::Slice;
pub use render::mesh::{VertexCount, ElementCount, VertexSlice, IndexSlice};
pub use Environment = render::envir::Storage;
pub use render::envir::{BlockVar, UniformVar, TextureVar};
pub use render::target::{ClearData, Plane, Frame, TextureLayer, TextureLevel};
pub use Device = device::Server;
pub use device::{Color, GraphicsContext, InitError};
pub use device::shade::{UniformValue, ValueI32, ValueF32, ValueI32Vec, ValueF32Vec, ValueF32Matrix};

mod render;
pub mod platform;

#[allow(visible_private_types)]
pub fn start<Api, P: GraphicsContext<Api>>(graphics_context: P, options: device::Options)
        -> Result<(Renderer, Device<P, device::Device>), InitError> {
    device::init(graphics_context, options).map(|(client, server)| {
        (Renderer::new(client), server)
    })
}
<|endoftext|>


<commit_before>#![allow(missing_docs)]
#![allow(dead_code)]

//! A first person camera.

use std::num::{Float, FromPrimitive};
use event::GenericEvent;
use input::Button;
use {
    input,
    Camera,
};
use vecmath::consts::Radians;

bitflags!(flags Keys: u8 {
    const MOVE_FORWARD  = 0b00000001,
    const MOVE_BACKWARD = 0b00000010,
    const STRAFE_LEFT   = 0b00000100,
    const STRAFE_RIGHT  = 0b00001000,
    const FLY_UP        = 0b00010000,
    const FLY_DOWN      = 0b00100000
});

/// First person camera settings.
pub struct FirstPersonSettings<T=f32> {
    /// Which button to press to move forward.
    pub move_forward_button: input::Button,
    /// Which button to press to move backward.
    pub move_backward_button: input::Button,
    /// Which button to press to strafe left.
    pub strafe_left_button: input::Button,
    /// Which button to press to strafe right.
    pub strafe_right_button: input::Button,
    /// Which button to press to fly up.
    pub fly_up_button: input::Button,
    /// Which button to press to fly down.
    pub fly_down_button: input::Button,
    /// Which button to press to move faster.
    pub move_faster_button: input::Button,
    /// The horizontal movement speed.
    ///
    /// This is measured in units per second.
    pub speed_horizontal: T,
    /// The vertical movement speed.
    ///
    /// This is measured in units per second.
    pub speed_vertical: T,
}

impl<T: Float> FirstPersonSettings<T> {
    /// Creates new first person camera settings with wasd defaults.
    pub fn keyboard_wasd() -> FirstPersonSettings<T> {
        use input::Button::Keyboard;
        use input::keyboard::Key;

        FirstPersonSettings {
            move_forward_button: Keyboard(Key::W),
            move_backward_button: Keyboard(Key::S),
            strafe_left_button: Keyboard(Key::A),
            strafe_right_button: Keyboard(Key::D),
            fly_up_button: Keyboard(Key::Space),
            fly_down_button: Keyboard(Key::LShift),
            move_faster_button: Keyboard(Key::LCtrl),
            speed_horizontal: Float::one(),
            speed_vertical: Float::one(),
        }
    }

    /// Creates a new first person camera settings with esdf defaults.
    pub fn keyboard_esdf() -> FirstPersonSettings<T> {
        use input::Button::Keyboard;
        use input::keyboard::Key;

        FirstPersonSettings {
            move_forward_button: Keyboard(Key::E),
            move_backward_button: Keyboard(Key::D),
            strafe_left_button: Keyboard(Key::S),
            strafe_right_button: Keyboard(Key::F),
            fly_up_button: Keyboard(Key::Space),
            fly_down_button: Keyboard(Key::Z),
            move_faster_button: Keyboard(Key::LShift),
            speed_horizontal: Float::one(),
            speed_vertical: Float::one(),
        }
    }
}

/// Models a flying first person camera.
pub struct FirstPerson<T=f32> {
    /// The first person camera settings.
    pub settings: FirstPersonSettings<T>,
    /// The yaw angle (in radians).
    pub yaw: T,
    /// The pitch angle (in radians).
    pub pitch: T,
    /// The direction we are heading.
    pub direction: [T; 3],
    /// The position of the camera.
    pub position: [T; 3],
    /// The velocity we are moving in the direction.
    pub velocity: T,
    /// The keys that are pressed.
    keys: Keys,
}

impl<T: Float + FromPrimitive + Copy + Radians>
FirstPerson<T> {

    /// Creates a new first person camera.
    pub fn new(
        position: [T; 3],
        settings: FirstPersonSettings<T>
    ) -> FirstPerson<T> {
        let _0: T = Float::zero();
        FirstPerson {
            settings: settings,
            yaw: _0,
            pitch: _0,
            keys: Keys::empty(),
            direction: [_0, _0, _0],
            position: position,
            velocity: Float::one(),
        }
    }

    /// Computes camera.
    pub fn camera(&self, dt: f64) -> Camera<T> {
        let dt: T = FromPrimitive::from_f64(dt).unwrap();
        let dh = dt * self.velocity * self.settings.speed_horizontal;
        let [dx, dy, dz] = self.direction;
        let (s, c) = (self.yaw.sin(), self.yaw.cos());
        let mut camera = Camera::new([
            self.position[0] + (s * dx - c * dz) * dh,
            self.position[1] + dy * dt * self.settings.speed_vertical,
            self.position[2] + (s * dz + c * dx) * dh
        ]);
        camera.set_yaw_pitch(self.yaw, self.pitch);
        camera
    }

    /// Handles game event and updates camera.
    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        use event::{ MouseRelativeEvent, PressEvent, ReleaseEvent, UpdateEvent };

        e.update(|args| {
            let cam = self.camera(args.dt);
            self.position = cam.position;
        });

        let &mut FirstPerson {
            ref mut yaw,
            ref mut pitch,
            ref mut keys,
            ref mut direction,
            ref mut velocity,
            ref settings,
            ..
        } = self;

        let pi: T = Radians::_180();
        let sqrt2: T = Float::sqrt(FromPrimitive::from_f64(2f64).unwrap());
        let _0: T = Float::zero();
        let _1: T = Float::one();
        let _2: T = FromPrimitive::from_int(2).unwrap();
        let _3: T = FromPrimitive::from_int(3).unwrap();
        let _4: T = FromPrimitive::from_int(4).unwrap();
        let _360: T = FromPrimitive::from_int(360).unwrap();
        e.mouse_relative(|dx, dy| {
            let dx: T = FromPrimitive::from_f64(dx).unwrap();
            let dy: T = FromPrimitive::from_f64(dy).unwrap();
            *yaw = (*yaw - dx / _360 * pi / _4) % (_2 * pi);
            *pitch = *pitch + dy / _360 * pi / _4;
            *pitch = (*pitch).min(pi / _2).max(-pi / _2);
        });
        e.press(|button| {
            let [dx, dy, dz] = *direction;
            let sgn = |&: x: T| if x == _0 { _0 } else { x.signum() };
            let mut set = |&mut: k, x: T, y: T, z: T| {
                let (x, z) = (sgn(x), sgn(z));
                let (x, z) = if x != _0 && z != _0 {
                    (x / sqrt2, z / sqrt2)
                } else {
                    (x, z)
                };
                *direction = [x, y, z];
                keys.insert(k);
            };
            match button {
                x if x == settings.move_forward_button =>
                    set(MOVE_FORWARD, -_1, dy, dz),
                x if x == settings.move_backward_button =>
                    set(MOVE_BACKWARD, _1, dy, dz),
                x if x == settings.strafe_left_button =>
                    set(STRAFE_LEFT, dx, dy, _1),
                x if x == settings.strafe_right_button =>
                    set(STRAFE_RIGHT, dx, dy, -_1),
                x if x == settings.fly_up_button =>
                    set(FLY_UP, dx, _1, dz),
                x if x == settings.fly_down_button =>
                    set(FLY_DOWN, dx, -_1, dz),
                x if x == settings.move_faster_button => *velocity = _2,
                _ => {}
            }
        });
        e.release(|button| {
            let [dx, dy, dz] = *direction;
            let sgn = |&: x: T| if x == _0 { _0 } else { x.signum() };
            let mut set = |&mut: x: T, y: T, z: T| {
                let (x, z) = (sgn(x), sgn(z));
                let (x, z) = if x != _0 && z != _0 {
                    (x / sqrt2, z / sqrt2)
                } else {
                    (x, z)
                };
                *direction = [x, y, z];
            };
            let mut release = |&mut: key, rev_key, rev_val| {
                keys.remove(key);
                if keys.contains(rev_key) { rev_val } else { _0 }
            };
            match button {
                x if x == settings.move_forward_button =>
                    set(release(MOVE_FORWARD, MOVE_BACKWARD, _1), dy, dz),
                x if x == settings.move_backward_button =>
                    set(release(MOVE_BACKWARD, MOVE_FORWARD, -_1), dy, dz),
                x if x == settings.strafe_left_button =>
                    set(dx, dy, release(STRAFE_LEFT, STRAFE_RIGHT, -_1)),
                x if x == settings.strafe_right_button =>
                    set(dx, dy, release(STRAFE_RIGHT, STRAFE_LEFT, _1)),
                x if x == settings.fly_up_button =>
                    set(dx, release(FLY_UP, FLY_DOWN, -_1), dz),
                x if x == settings.fly_down_button =>
                    set(dx, release(FLY_DOWN, FLY_UP, _1), dz),
                x if x == settings.move_faster_button => *velocity = _1,
                _ => {}
            }
        });
    }
}

<commit_msg>Fix closure syntax errors.<commit_after>#![allow(missing_docs)]
#![allow(dead_code)]

//! A first person camera.

use std::num::{Float, FromPrimitive};
use event::GenericEvent;
use input::Button;
use {
    input,
    Camera,
};
use vecmath::consts::Radians;

bitflags!(flags Keys: u8 {
    const MOVE_FORWARD  = 0b00000001,
    const MOVE_BACKWARD = 0b00000010,
    const STRAFE_LEFT   = 0b00000100,
    const STRAFE_RIGHT  = 0b00001000,
    const FLY_UP        = 0b00010000,
    const FLY_DOWN      = 0b00100000
});

/// First person camera settings.
pub struct FirstPersonSettings<T=f32> {
    /// Which button to press to move forward.
    pub move_forward_button: input::Button,
    /// Which button to press to move backward.
    pub move_backward_button: input::Button,
    /// Which button to press to strafe left.
    pub strafe_left_button: input::Button,
    /// Which button to press to strafe right.
    pub strafe_right_button: input::Button,
    /// Which button to press to fly up.
    pub fly_up_button: input::Button,
    /// Which button to press to fly down.
    pub fly_down_button: input::Button,
    /// Which button to press to move faster.
    pub move_faster_button: input::Button,
    /// The horizontal movement speed.
    ///
    /// This is measured in units per second.
    pub speed_horizontal: T,
    /// The vertical movement speed.
    ///
    /// This is measured in units per second.
    pub speed_vertical: T,
}

impl<T: Float> FirstPersonSettings<T> {
    /// Creates new first person camera settings with wasd defaults.
    pub fn keyboard_wasd() -> FirstPersonSettings<T> {
        use input::Button::Keyboard;
        use input::keyboard::Key;

        FirstPersonSettings {
            move_forward_button: Keyboard(Key::W),
            move_backward_button: Keyboard(Key::S),
            strafe_left_button: Keyboard(Key::A),
            strafe_right_button: Keyboard(Key::D),
            fly_up_button: Keyboard(Key::Space),
            fly_down_button: Keyboard(Key::LShift),
            move_faster_button: Keyboard(Key::LCtrl),
            speed_horizontal: Float::one(),
            speed_vertical: Float::one(),
        }
    }

    /// Creates a new first person camera settings with esdf defaults.
    pub fn keyboard_esdf() -> FirstPersonSettings<T> {
        use input::Button::Keyboard;
        use input::keyboard::Key;

        FirstPersonSettings {
            move_forward_button: Keyboard(Key::E),
            move_backward_button: Keyboard(Key::D),
            strafe_left_button: Keyboard(Key::S),
            strafe_right_button: Keyboard(Key::F),
            fly_up_button: Keyboard(Key::Space),
            fly_down_button: Keyboard(Key::Z),
            move_faster_button: Keyboard(Key::LShift),
            speed_horizontal: Float::one(),
            speed_vertical: Float::one(),
        }
    }
}

/// Models a flying first person camera.
pub struct FirstPerson<T=f32> {
    /// The first person camera settings.
    pub settings: FirstPersonSettings<T>,
    /// The yaw angle (in radians).
    pub yaw: T,
    /// The pitch angle (in radians).
    pub pitch: T,
    /// The direction we are heading.
    pub direction: [T; 3],
    /// The position of the camera.
    pub position: [T; 3],
    /// The velocity we are moving in the direction.
    pub velocity: T,
    /// The keys that are pressed.
    keys: Keys,
}

impl<T: Float + FromPrimitive + Copy + Radians>
FirstPerson<T> {

    /// Creates a new first person camera.
    pub fn new(
        position: [T; 3],
        settings: FirstPersonSettings<T>
    ) -> FirstPerson<T> {
        let _0: T = Float::zero();
        FirstPerson {
            settings: settings,
            yaw: _0,
            pitch: _0,
            keys: Keys::empty(),
            direction: [_0, _0, _0],
            position: position,
            velocity: Float::one(),
        }
    }

    /// Computes camera.
    pub fn camera(&self, dt: f64) -> Camera<T> {
        let dt: T = FromPrimitive::from_f64(dt).unwrap();
        let dh = dt * self.velocity * self.settings.speed_horizontal;
        let [dx, dy, dz] = self.direction;
        let (s, c) = (self.yaw.sin(), self.yaw.cos());
        let mut camera = Camera::new([
            self.position[0] + (s * dx - c * dz) * dh,
            self.position[1] + dy * dt * self.settings.speed_vertical,
            self.position[2] + (s * dz + c * dx) * dh
        ]);
        camera.set_yaw_pitch(self.yaw, self.pitch);
        camera
    }

    /// Handles game event and updates camera.
    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        use event::{ MouseRelativeEvent, PressEvent, ReleaseEvent, UpdateEvent };

        e.update(|args| {
            let cam = self.camera(args.dt);
            self.position = cam.position;
        });

        let &mut FirstPerson {
            ref mut yaw,
            ref mut pitch,
            ref mut keys,
            ref mut direction,
            ref mut velocity,
            ref settings,
            ..
        } = self;

        let pi: T = Radians::_180();
        let sqrt2: T = Float::sqrt(FromPrimitive::from_f64(2f64).unwrap());
        let _0: T = Float::zero();
        let _1: T = Float::one();
        let _2: T = FromPrimitive::from_int(2).unwrap();
        let _3: T = FromPrimitive::from_int(3).unwrap();
        let _4: T = FromPrimitive::from_int(4).unwrap();
        let _360: T = FromPrimitive::from_int(360).unwrap();
        e.mouse_relative(|dx, dy| {
            let dx: T = FromPrimitive::from_f64(dx).unwrap();
            let dy: T = FromPrimitive::from_f64(dy).unwrap();
            *yaw = (*yaw - dx / _360 * pi / _4) % (_2 * pi);
            *pitch = *pitch + dy / _360 * pi / _4;
            *pitch = (*pitch).min(pi / _2).max(-pi / _2);
        });
        e.press(|button| {
            let [dx, dy, dz] = *direction;
            let sgn = |&: x: T| if x == _0 { _0 } else { x.signum() };
            let mut set = |k, x: T, y: T, z: T| {
                let (x, z) = (sgn(x), sgn(z));
                let (x, z) = if x != _0 && z != _0 {
                    (x / sqrt2, z / sqrt2)
                } else {
                    (x, z)
                };
                *direction = [x, y, z];
                keys.insert(k);
            };
            match button {
                x if x == settings.move_forward_button =>
                    set(MOVE_FORWARD, -_1, dy, dz),
                x if x == settings.move_backward_button =>
                    set(MOVE_BACKWARD, _1, dy, dz),
                x if x == settings.strafe_left_button =>
                    set(STRAFE_LEFT, dx, dy, _1),
                x if x == settings.strafe_right_button =>
                    set(STRAFE_RIGHT, dx, dy, -_1),
                x if x == settings.fly_up_button =>
                    set(FLY_UP, dx, _1, dz),
                x if x == settings.fly_down_button =>
                    set(FLY_DOWN, dx, -_1, dz),
                x if x == settings.move_faster_button => *velocity = _2,
                _ => {}
            }
        });
        e.release(|button| {
            let [dx, dy, dz] = *direction;
            let sgn = |&: x: T| if x == _0 { _0 } else { x.signum() };
            let mut set = |x: T, y: T, z: T| {
                let (x, z) = (sgn(x), sgn(z));
                let (x, z) = if x != _0 && z != _0 {
                    (x / sqrt2, z / sqrt2)
                } else {
                    (x, z)
                };
                *direction = [x, y, z];
            };
            let mut release = |key, rev_key, rev_val| {
                keys.remove(key);
                if keys.contains(rev_key) { rev_val } else { _0 }
            };
            match button {
                x if x == settings.move_forward_button =>
                    set(release(MOVE_FORWARD, MOVE_BACKWARD, _1), dy, dz),
                x if x == settings.move_backward_button =>
                    set(release(MOVE_BACKWARD, MOVE_FORWARD, -_1), dy, dz),
                x if x == settings.strafe_left_button =>
                    set(dx, dy, release(STRAFE_LEFT, STRAFE_RIGHT, -_1)),
                x if x == settings.strafe_right_button =>
                    set(dx, dy, release(STRAFE_RIGHT, STRAFE_LEFT, _1)),
                x if x == settings.fly_up_button =>
                    set(dx, release(FLY_UP, FLY_DOWN, -_1), dz),
                x if x == settings.fly_down_button =>
                    set(dx, release(FLY_DOWN, FLY_UP, _1), dz),
                x if x == settings.move_faster_button => *velocity = _1,
                _ => {}
            }
        });
    }
}

<|endoftext|>