<commit_before>iface iterable<A> {
    fn iter(blk: fn(A));
}

impl<A> of iterable<A> for fn@(fn(A)) {
    fn iter(blk: fn(A)) {
        self(blk);
    }
}

// accomodate the fact that int/uint are passed by value by default:
impl of iterable<int> for fn@(fn(int)) {
    fn iter(blk: fn(&&int)) {
        self {|i| blk(i)}
    }
}

impl of iterable<uint> for fn@(fn(uint)) {
    fn iter(blk: fn(&&uint)) {
        self {|i| blk(i)}
    }
}

impl<A> of iterable<A> for [A] {
    fn iter(blk: fn(A)) {
        vec::iter(self, blk)
    }
}

impl<A> of iterable<A> for option<A> {
    fn iter(blk: fn(A)) {
        option::may(self, blk)
    }
}

impl of iterable<char> for str {
    fn iter(blk: fn(&&char)) {
        str::chars_iter(self) { |ch| blk(ch) }
    }
}

fn enumerate<A,IA:iterable<A>>(self: IA, blk: fn(uint, A)) {
    let i = 0u;
    self.iter {|a|
        blk(i, a);
        i += 1u;
    }
}

// Here: we have to use fn@ for predicates and map functions, because
// we will be binding them up into a closure.  Disappointing.  A true
// region type system might be able to do better than this.

fn filter<A,IA:iterable<A>>(self: IA, prd: fn@(A) -> bool, blk: fn(A)) {
    self.iter {|a|
        if prd(a) { blk(a) }
    }
}

fn map<A,B,IA:iterable<A>>(self: IA, cnv: fn@(A) -> B, blk: fn(B)) {
    self.iter {|a|
        let b = cnv(a);
        blk(b);
    }
}

fn flat_map<A,B,IA:iterable<A>,IB:iterable<B>>(
    self: IA, cnv: fn@(A) -> IB, blk: fn(B)) {
    self.iter {|a|
        cnv(a).iter(blk)
    }
}

fn foldl<A,B,IA:iterable<A>>(self: IA, +b0: B, blk: fn(-B, A) -> B) -> B {
    let b <- b0;
    self.iter {|a|
        b = blk(b, a);
    }
    ret b;
}

fn foldr<A:copy,B,IA:iterable<A>>(
    self: IA, +b0: B, blk: fn(A, -B) -> B) -> B {

    let b <- b0;
    reversed(self) {|a|
        b = blk(a, b);
    }
    ret b;
}

fn to_list<A:copy,IA:iterable<A>>(self: IA) -> [A] {
    foldl::<A,[A],IA>(self, [], {|r, a| r + [a]})
}

// FIXME: This could be made more efficient with an riterable interface
fn reversed<A:copy,IA:iterable<A>>(self: IA, blk: fn(A)) {
    vec::riter(to_list(self), blk)
}

fn count<A,IA:iterable<A>>(self: IA, x: A) -> uint {
    foldl(self, 0u) {|count, value|
        if value == x {
            count + 1u
        } else {
            count
        }
    }
}

fn repeat(times: uint, blk: fn()) {
    let i = 0u;
    while i < times {
        blk();
        i += 1u;
    }
}

fn min<A:copy,IA:iterable<A>>(self: IA) -> A {
    alt foldl::<A,option<A>,IA>(self, none) {|a, b|
        alt a {
          some(a_) if a_ < b {
            // FIXME: Not sure if this is successfully optimized to a move
            a
          }
          _ { some(b) }
        }
    } {
        some(val) { val }
        none { fail "min called on empty iterator" }
    }
}

fn max<A:copy,IA:iterable<A>>(self: IA) -> A {
    alt foldl::<A,option<A>,IA>(self, none) {|a, b|
        alt a {
          some(a_) if a_ > b {
            // FIXME: Not sure if this is successfully optimized to a move
            a
          }
          _ { some(b) }
        }
    } {
        some(val) { val }
        none { fail "max called on empty iterator" }
    }
}

#[test]
fn test_enumerate() {
    enumerate(["0", "1", "2"]) {|i,j|
        assert #fmt["%u",i] == j;
    }
}

#[test]
fn test_map_and_to_list() {
    let a = bind vec::iter([0, 1, 2], _);
    let b = bind map(a, {|i| i*2}, _);
    let c = to_list(b);
    assert c == [0, 2, 4];
}

#[test]
fn test_map_directly_on_vec() {
    let b = bind map([0, 1, 2], {|i| i*2}, _);
    let c = to_list(b);
    assert c == [0, 2, 4];
}

#[test]
fn test_filter_on_int_range() {
    fn is_even(&&i: int) -> bool {
        ret (i % 2) == 0;
    }

    let l = to_list(bind filter(bind int::range(0, 10, _), is_even, _));
    assert l == [0, 2, 4, 6, 8];
}

#[test]
fn test_filter_on_uint_range() {
    fn is_even(&&i: uint) -> bool {
        ret (i % 2u) == 0u;
    }

    let l = to_list(bind filter(bind uint::range(0u, 10u, _), is_even, _));
    assert l == [0u, 2u, 4u, 6u, 8u];
}

#[test]
fn test_flat_map_with_option() {
    fn if_even(&&i: int) -> option<int> {
        if (i % 2) == 0 { some(i) }
        else { none }
    }

    let a = bind vec::iter([0, 1, 2], _);
    let b = bind flat_map(a, if_even, _);
    let c = to_list(b);
    assert c == [0, 2];
}

#[test]
fn test_flat_map_with_list() {
    fn repeat(&&i: int) -> [int] {
        let r = [];
        int::range(0, i) {|_j| r += [i]; }
        r
    }

    let a = bind vec::iter([0, 1, 2, 3], _);
    let b = bind flat_map(a, repeat, _);
    let c = to_list(b);
    #debug["c = %?", c];
    assert c == [1, 2, 2, 3, 3, 3];
}

#[test]
fn test_repeat() {
    let c = [],
        i = 0u;
    repeat(5u) {||
        c += [(i * i)];
        i += 1u;
    };
    #debug["c = %?", c];
    assert c == [0u, 1u, 4u, 9u, 16u];
}

#[test]
fn test_min() {
    assert min([5, 4, 1, 2, 3]) == 1;
}

#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_min_empty() {
    min::<int, [int]>([]);
}

#[test]
fn test_max() {
    assert max([1, 2, 4, 2, 3]) == 4;
}

#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_max_empty() {
    max::<int, [int]>([]);
}

#[test]
fn test_reversed() {
    assert to_list(bind reversed([1, 2, 3], _)) == [3, 2, 1];
}

#[test]
fn test_count() {
    assert count([1, 2, 1, 2, 1], 1) == 3u;
}

#[test]
fn test_foldr() {
    fn sub(&&a: int, -b: int) -> int {
        a - b
    }
    let sum = foldr([1, 2, 3, 4], 0, sub);
    assert sum == -2;
}<commit_msg>core: Add iter::filter_map<commit_after>iface iterable<A> {
    fn iter(blk: fn(A));
}

impl<A> of iterable<A> for fn@(fn(A)) {
    fn iter(blk: fn(A)) {
        self(blk);
    }
}

// accomodate the fact that int/uint are passed by value by default:
impl of iterable<int> for fn@(fn(int)) {
    fn iter(blk: fn(&&int)) {
        self {|i| blk(i)}
    }
}

impl of iterable<uint> for fn@(fn(uint)) {
    fn iter(blk: fn(&&uint)) {
        self {|i| blk(i)}
    }
}

impl<A> of iterable<A> for [A] {
    fn iter(blk: fn(A)) {
        vec::iter(self, blk)
    }
}

impl<A> of iterable<A> for option<A> {
    fn iter(blk: fn(A)) {
        option::may(self, blk)
    }
}

impl of iterable<char> for str {
    fn iter(blk: fn(&&char)) {
        str::chars_iter(self) { |ch| blk(ch) }
    }
}

fn enumerate<A,IA:iterable<A>>(self: IA, blk: fn(uint, A)) {
    let i = 0u;
    self.iter {|a|
        blk(i, a);
        i += 1u;
    }
}

// Here: we have to use fn@ for predicates and map functions, because
// we will be binding them up into a closure.  Disappointing.  A true
// region type system might be able to do better than this.

fn filter<A,IA:iterable<A>>(self: IA, prd: fn@(A) -> bool, blk: fn(A)) {
    self.iter {|a|
        if prd(a) { blk(a) }
    }
}

fn filter_map<A,B,IA:iterable<A>>(self: IA, cnv: fn@(A) -> option<B>,
                                  blk: fn(B)) {
    self.iter {|a|
        alt cnv(a) {
          some(b) { blk(b) }
          none { }
        }
    }
}

fn map<A,B,IA:iterable<A>>(self: IA, cnv: fn@(A) -> B, blk: fn(B)) {
    self.iter {|a|
        let b = cnv(a);
        blk(b);
    }
}

fn flat_map<A,B,IA:iterable<A>,IB:iterable<B>>(
    self: IA, cnv: fn@(A) -> IB, blk: fn(B)) {
    self.iter {|a|
        cnv(a).iter(blk)
    }
}

fn foldl<A,B,IA:iterable<A>>(self: IA, +b0: B, blk: fn(-B, A) -> B) -> B {
    let b <- b0;
    self.iter {|a|
        b = blk(b, a);
    }
    ret b;
}

fn foldr<A:copy,B,IA:iterable<A>>(
    self: IA, +b0: B, blk: fn(A, -B) -> B) -> B {

    let b <- b0;
    reversed(self) {|a|
        b = blk(a, b);
    }
    ret b;
}

fn to_list<A:copy,IA:iterable<A>>(self: IA) -> [A] {
    foldl::<A,[A],IA>(self, [], {|r, a| r + [a]})
}

// FIXME: This could be made more efficient with an riterable interface
fn reversed<A:copy,IA:iterable<A>>(self: IA, blk: fn(A)) {
    vec::riter(to_list(self), blk)
}

fn count<A,IA:iterable<A>>(self: IA, x: A) -> uint {
    foldl(self, 0u) {|count, value|
        if value == x {
            count + 1u
        } else {
            count
        }
    }
}

fn repeat(times: uint, blk: fn()) {
    let i = 0u;
    while i < times {
        blk();
        i += 1u;
    }
}

fn min<A:copy,IA:iterable<A>>(self: IA) -> A {
    alt foldl::<A,option<A>,IA>(self, none) {|a, b|
        alt a {
          some(a_) if a_ < b {
            // FIXME: Not sure if this is successfully optimized to a move
            a
          }
          _ { some(b) }
        }
    } {
        some(val) { val }
        none { fail "min called on empty iterator" }
    }
}

fn max<A:copy,IA:iterable<A>>(self: IA) -> A {
    alt foldl::<A,option<A>,IA>(self, none) {|a, b|
        alt a {
          some(a_) if a_ > b {
            // FIXME: Not sure if this is successfully optimized to a move
            a
          }
          _ { some(b) }
        }
    } {
        some(val) { val }
        none { fail "max called on empty iterator" }
    }
}

#[test]
fn test_enumerate() {
    enumerate(["0", "1", "2"]) {|i,j|
        assert #fmt["%u",i] == j;
    }
}

#[test]
fn test_map_and_to_list() {
    let a = bind vec::iter([0, 1, 2], _);
    let b = bind map(a, {|i| i*2}, _);
    let c = to_list(b);
    assert c == [0, 2, 4];
}

#[test]
fn test_map_directly_on_vec() {
    let b = bind map([0, 1, 2], {|i| i*2}, _);
    let c = to_list(b);
    assert c == [0, 2, 4];
}

#[test]
fn test_filter_on_int_range() {
    fn is_even(&&i: int) -> bool {
        ret (i % 2) == 0;
    }

    let l = to_list(bind filter(bind int::range(0, 10, _), is_even, _));
    assert l == [0, 2, 4, 6, 8];
}

#[test]
fn test_filter_on_uint_range() {
    fn is_even(&&i: uint) -> bool {
        ret (i % 2u) == 0u;
    }

    let l = to_list(bind filter(bind uint::range(0u, 10u, _), is_even, _));
    assert l == [0u, 2u, 4u, 6u, 8u];
}

#[test]
fn test_filter_map() {
    fn negativate_the_evens(&&i: int) -> option<int> {
        if i % 2 == 0 {
            some(-i)
        } else {
            none
        }
    }

    let l = to_list(bind filter_map(
        bind int::range(0, 5, _), negativate_the_evens, _));
    assert l == [0, -2, -4];
}

#[test]
fn test_flat_map_with_option() {
    fn if_even(&&i: int) -> option<int> {
        if (i % 2) == 0 { some(i) }
        else { none }
    }

    let a = bind vec::iter([0, 1, 2], _);
    let b = bind flat_map(a, if_even, _);
    let c = to_list(b);
    assert c == [0, 2];
}

#[test]
fn test_flat_map_with_list() {
    fn repeat(&&i: int) -> [int] {
        let r = [];
        int::range(0, i) {|_j| r += [i]; }
        r
    }

    let a = bind vec::iter([0, 1, 2, 3], _);
    let b = bind flat_map(a, repeat, _);
    let c = to_list(b);
    #debug["c = %?", c];
    assert c == [1, 2, 2, 3, 3, 3];
}

#[test]
fn test_repeat() {
    let c = [],
        i = 0u;
    repeat(5u) {||
        c += [(i * i)];
        i += 1u;
    };
    #debug["c = %?", c];
    assert c == [0u, 1u, 4u, 9u, 16u];
}

#[test]
fn test_min() {
    assert min([5, 4, 1, 2, 3]) == 1;
}

#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_min_empty() {
    min::<int, [int]>([]);
}

#[test]
fn test_max() {
    assert max([1, 2, 4, 2, 3]) == 4;
}

#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_max_empty() {
    max::<int, [int]>([]);
}

#[test]
fn test_reversed() {
    assert to_list(bind reversed([1, 2, 3], _)) == [3, 2, 1];
}

#[test]
fn test_count() {
    assert count([1, 2, 1, 2, 1], 1) == 3u;
}

#[test]
fn test_foldr() {
    fn sub(&&a: int, -b: int) -> int {
        a - b
    }
    let sum = foldr([1, 2, 3, 4], 0, sub);
    assert sum == -2;
}<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// rust - central access to other rust tools
// FIXME #2238 Make commands run and test emit proper file endings on windows
// FIXME #2238 Make run only accept source that emits an executable

#[link(name = "rust",
       vers = "0.8-pre",
       uuid = "4a24da33-5cc8-4037-9352-2cbe9bd9d27c",
       url = "https://github.com/mozilla/rust/tree/master/src/rust")];

#[license = "MIT/ASL2"];
#[crate_type = "lib"];

extern mod rustpkg;
extern mod rustdoc;
extern mod rusti;
extern mod rustc;

use std::io;
use std::os;
use std::run;
use std::libc::exit;

enum ValidUsage {
    Valid(int), Invalid
}

impl ValidUsage {
    fn is_valid(&self) -> bool {
        match *self {
            Valid(_)   => true,
            Invalid    => false
        }
    }
}

enum Action {
    Call(extern "Rust" fn(args: &[~str]) -> ValidUsage),
    CallMain(&'static str, extern "Rust" fn()),
}

enum UsageSource<'self> {
    UsgStr(&'self str),
    UsgCall(extern "Rust" fn()),
}

struct Command<'self> {
    cmd: &'self str,
    action: Action,
    usage_line: &'self str,
    usage_full: UsageSource<'self>,
}

static COMMANDS: &'static [Command<'static>] = &[
    Command{
        cmd: "build",
        action: CallMain("rustc", rustc::main),
        usage_line: "compile rust source files",
        usage_full: UsgCall(rustc_help),
    },
    Command{
        cmd: "run",
        action: Call(cmd_run),
        usage_line: "build an executable, and run it",
        usage_full: UsgStr(
            "The run command is an shortcut for the command line \n\
             \"rustc <filename> -o <filestem>~ && ./<filestem>~ [<arguments>...]\".\
            \n\nUsage:\trust run <filename> [<arguments>...]"
        )
    },
    Command{
        cmd: "test",
        action: Call(cmd_test),
        usage_line: "build a test executable, and run it",
        usage_full: UsgStr(
            "The test command is an shortcut for the command line \n\
            \"rustc --test <filename> -o <filestem>test~ && \
            ./<filestem>test~\"\n\nUsage:\trust test <filename>"
        )
    },
    Command{
        cmd: "doc",
        action: CallMain("rustdoc", rustdoc::main),
        usage_line: "generate documentation from doc comments",
        usage_full: UsgCall(rustdoc::config::usage),
    },
    Command{
        cmd: "pkg",
        action: CallMain("rustpkg", rustpkg::main),
        usage_line: "download, build, install rust packages",
        usage_full: UsgCall(rustpkg::usage::general),
    },
    Command{
        cmd: "sketch",
        action: CallMain("rusti", rusti::main),
        usage_line: "run a rust interpreter",
        usage_full: UsgStr("\nUsage:\trusti"),
    },
    Command{
        cmd: "help",
        action: Call(cmd_help),
        usage_line: "show detailed usage of a command",
        usage_full: UsgStr(
            "The help command displays the usage text of another command.\n\
            The text is either build in, or provided by the corresponding \
            program.\n\nUsage:\trust help <command>"
        )
    }
];

fn rustc_help() {
    rustc::usage(os::args()[0].clone())
}

fn find_cmd(command_string: &str) -> Option<Command> {
    do COMMANDS.iter().find_ |command| {
        command.cmd == command_string
    }.map_consume(|x| *x)
}

fn cmd_help(args: &[~str]) -> ValidUsage {
    fn print_usage(command_string: ~str) -> ValidUsage {
        match find_cmd(command_string) {
            Some(command) => {
                match command.action {
                    CallMain(prog, _) => printfln!(
                        "The %s command is an alias for the %s program.",
                        command.cmd, prog),
                    _       => ()
                }
                match command.usage_full {
                    UsgStr(msg) => printfln!("%s\n", msg),
                    UsgCall(f)  => f(),
                }
                Valid(0)
            },
            None => Invalid
        }
    }

    match args {
        [ref command_string] => print_usage((*command_string).clone()),
        _                    => Invalid
    }
}

fn cmd_test(args: &[~str]) -> ValidUsage {
    match args {
        [ref filename] => {
            let test_exec = Path(*filename).filestem().unwrap() + "test~";
            invoke("rustc", &[~"--test", filename.to_owned(),
                              ~"-o", test_exec.to_owned()], rustc::main);
            let exit_code = run::process_status(~"./" + test_exec, []);
            Valid(exit_code)
        }
        _ => Invalid
    }
}

fn cmd_run(args: &[~str]) -> ValidUsage {
    match args {
        [ref filename, ..prog_args] => {
            let exec = Path(*filename).filestem().unwrap() + "~";
            invoke("rustc", &[filename.to_owned(), ~"-o", exec.to_owned()],
                   rustc::main);
            let exit_code = run::process_status(~"./"+exec, prog_args);
            Valid(exit_code)
        }
        _ => Invalid
    }
}

fn invoke(prog: &str, args: &[~str], f: &fn()) {
    let mut osargs = ~[prog.to_owned()];
    osargs.push_all_move(args.to_owned());
    os::set_args(osargs);
    f();
}

fn do_command(command: &Command, args: &[~str]) -> ValidUsage {
    match command.action {
        Call(f) => f(args),
        CallMain(prog, f) => {
            invoke(prog, args, f);
            Valid(0)
        }
    }
}

fn usage() {
    static INDENT: uint = 8;

    io::print(
        "The rust tool is a convenience for managing rust source code.\n\
        It acts as a shortcut for programs of the rust tool chain.\n\
        \n\
        Usage:\trust <command> [arguments]\n\
        \n\
        The commands are:\n\
        \n"
    );

    for COMMANDS.iter().advance |command| {
        let padding = " ".repeat(INDENT - command.cmd.len());
        printfln!("    %s%s%s", command.cmd, padding, command.usage_line);
    }

    io::print(
        "\n\
        Use \"rust help <command>\" for more information about a command.\n\
        \n"
    );

}

pub fn main() {
    let os_args = os::args();

    if (os_args.len() > 1 && (os_args[1] == ~"-v" || os_args[1] == ~"--version")) {
        rustc::version(os_args[0]);
        unsafe { exit(0); }
    }

    let args = os_args.tail();

    if !args.is_empty() {
        let r = find_cmd(*args.head());
        for r.iter().advance |command| {
            let result = do_command(command, args.tail());
            match result {
                Valid(exit_code) => unsafe { exit(exit_code.to_i32()) },
                _                => loop
            }
        }
    }

    usage();
}
<commit_msg>auto merge of #8086 : luqmana/rust/rhelp, r=Aatch<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// rust - central access to other rust tools
// FIXME #2238 Make commands run and test emit proper file endings on windows
// FIXME #2238 Make run only accept source that emits an executable

#[link(name = "rust",
       vers = "0.8-pre",
       uuid = "4a24da33-5cc8-4037-9352-2cbe9bd9d27c",
       url = "https://github.com/mozilla/rust/tree/master/src/rust")];

#[license = "MIT/ASL2"];
#[crate_type = "lib"];

extern mod rustpkg;
extern mod rustdoc;
extern mod rusti;
extern mod rustc;

use std::io;
use std::os;
use std::run;
use std::libc::exit;

enum ValidUsage {
    Valid(int), Invalid
}

impl ValidUsage {
    fn is_valid(&self) -> bool {
        match *self {
            Valid(_)   => true,
            Invalid    => false
        }
    }
}

enum Action {
    Call(extern "Rust" fn(args: &[~str]) -> ValidUsage),
    CallMain(&'static str, extern "Rust" fn()),
}

enum UsageSource<'self> {
    UsgStr(&'self str),
    UsgCall(extern "Rust" fn()),
}

struct Command<'self> {
    cmd: &'self str,
    action: Action,
    usage_line: &'self str,
    usage_full: UsageSource<'self>,
}

static NUM_OF_COMMANDS: uint = 7;

// FIXME(#7617): should just be &'static [Command<'static>]
// but mac os doesn't seem to like that and tries to loop
// past the end of COMMANDS in usage thus passing garbage
// to str::repeat and eventually malloc and crashing.
static COMMANDS: [Command<'static>, .. NUM_OF_COMMANDS] = [
    Command{
        cmd: "build",
        action: CallMain("rustc", rustc::main),
        usage_line: "compile rust source files",
        usage_full: UsgCall(rustc_help),
    },
    Command{
        cmd: "run",
        action: Call(cmd_run),
        usage_line: "build an executable, and run it",
        usage_full: UsgStr(
            "The run command is an shortcut for the command line \n\
             \"rustc <filename> -o <filestem>~ && ./<filestem>~ [<arguments>...]\".\
            \n\nUsage:\trust run <filename> [<arguments>...]"
        )
    },
    Command{
        cmd: "test",
        action: Call(cmd_test),
        usage_line: "build a test executable, and run it",
        usage_full: UsgStr(
            "The test command is an shortcut for the command line \n\
            \"rustc --test <filename> -o <filestem>test~ && \
            ./<filestem>test~\"\n\nUsage:\trust test <filename>"
        )
    },
    Command{
        cmd: "doc",
        action: CallMain("rustdoc", rustdoc::main),
        usage_line: "generate documentation from doc comments",
        usage_full: UsgCall(rustdoc::config::usage),
    },
    Command{
        cmd: "pkg",
        action: CallMain("rustpkg", rustpkg::main),
        usage_line: "download, build, install rust packages",
        usage_full: UsgCall(rustpkg::usage::general),
    },
    Command{
        cmd: "sketch",
        action: CallMain("rusti", rusti::main),
        usage_line: "run a rust interpreter",
        usage_full: UsgStr("\nUsage:\trusti"),
    },
    Command{
        cmd: "help",
        action: Call(cmd_help),
        usage_line: "show detailed usage of a command",
        usage_full: UsgStr(
            "The help command displays the usage text of another command.\n\
            The text is either build in, or provided by the corresponding \
            program.\n\nUsage:\trust help <command>"
        )
    }
];

fn rustc_help() {
    rustc::usage(os::args()[0].clone())
}

fn find_cmd(command_string: &str) -> Option<Command> {
    do COMMANDS.iter().find_ |command| {
        command.cmd == command_string
    }.map_consume(|x| *x)
}

fn cmd_help(args: &[~str]) -> ValidUsage {
    fn print_usage(command_string: ~str) -> ValidUsage {
        match find_cmd(command_string) {
            Some(command) => {
                match command.action {
                    CallMain(prog, _) => printfln!(
                        "The %s command is an alias for the %s program.",
                        command.cmd, prog),
                    _       => ()
                }
                match command.usage_full {
                    UsgStr(msg) => printfln!("%s\n", msg),
                    UsgCall(f)  => f(),
                }
                Valid(0)
            },
            None => Invalid
        }
    }

    match args {
        [ref command_string] => print_usage((*command_string).clone()),
        _                    => Invalid
    }
}

fn cmd_test(args: &[~str]) -> ValidUsage {
    match args {
        [ref filename] => {
            let test_exec = Path(*filename).filestem().unwrap() + "test~";
            invoke("rustc", &[~"--test", filename.to_owned(),
                              ~"-o", test_exec.to_owned()], rustc::main);
            let exit_code = run::process_status(~"./" + test_exec, []);
            Valid(exit_code)
        }
        _ => Invalid
    }
}

fn cmd_run(args: &[~str]) -> ValidUsage {
    match args {
        [ref filename, ..prog_args] => {
            let exec = Path(*filename).filestem().unwrap() + "~";
            invoke("rustc", &[filename.to_owned(), ~"-o", exec.to_owned()],
                   rustc::main);
            let exit_code = run::process_status(~"./"+exec, prog_args);
            Valid(exit_code)
        }
        _ => Invalid
    }
}

fn invoke(prog: &str, args: &[~str], f: &fn()) {
    let mut osargs = ~[prog.to_owned()];
    osargs.push_all_move(args.to_owned());
    os::set_args(osargs);
    f();
}

fn do_command(command: &Command, args: &[~str]) -> ValidUsage {
    match command.action {
        Call(f) => f(args),
        CallMain(prog, f) => {
            invoke(prog, args, f);
            Valid(0)
        }
    }
}

fn usage() {
    static INDENT: uint = 8;

    io::print(
        "The rust tool is a convenience for managing rust source code.\n\
        It acts as a shortcut for programs of the rust tool chain.\n\
        \n\
        Usage:\trust <command> [arguments]\n\
        \n\
        The commands are:\n\
        \n"
    );

    for COMMANDS.iter().advance |command| {
        let padding = " ".repeat(INDENT - command.cmd.len());
        printfln!("    %s%s%s", command.cmd, padding, command.usage_line);
    }

    io::print(
        "\n\
        Use \"rust help <command>\" for more information about a command.\n\
        \n"
    );

}

pub fn main() {
    let os_args = os::args();

    if (os_args.len() > 1 && (os_args[1] == ~"-v" || os_args[1] == ~"--version")) {
        rustc::version(os_args[0]);
        unsafe { exit(0); }
    }

    let args = os_args.tail();

    if !args.is_empty() {
        let r = find_cmd(*args.head());
        for r.iter().advance |command| {
            let result = do_command(command, args.tail());
            match result {
                Valid(exit_code) => unsafe { exit(exit_code.to_i32()) },
                _                => loop
            }
        }
    }

    usage();
}
<|endoftext|>


<commit_before>#[doc = "
High-level bindings to work with the libuv library.

This module is geared towards library developers who want to
provide a high-level, abstracted interface to some set of
libuv functionality.
"];

import ll = uv_ll;

export high_level_loop;
export interact, prepare_loop;

#[doc = "
Used to abstract-away direct interaction with a libuv loop.

# Arguments

* async_handle - a pointer to a uv_async_t struct used to 'poke'
the C uv loop to process any pending callbacks

* op_chan - a channel used to send function callbacks to be processed
by the C uv loop
"]
type high_level_loop = {
    async_handle: *ll::uv_async_t,
    op_chan: comm::chan<fn~(*libc::c_void)>
};

#[doc = "
Pass in a callback to be processed on the running libuv loop's thread

# Arguments

* a_loop - a high_level_loop record that represents a channel of
communication with an active libuv loop running on a thread
somwhere in the current process

* cb - a function callback to be processed on the running loop's
thread. The only parameter is an opaque pointer to the running
uv_loop_t. You can use this pointer to initiate or continue any
operations against the loop
"]
unsafe fn interact(a_loop: high_level_loop,
                      -cb: fn~(*libc::c_void)) {
    comm::send(a_loop.op_chan, cb);
    ll::async_send(a_loop.async_handle);
}

#[doc = "
Prepares a clean, inactive uv_loop_t* to be used with any of the
functions in the `uv::hl` module.

Library developers can use this function to prepare a given
`uv_loop_t*`, whose lifecycle they manage, to be used, ran
and controlled with the tools in this module.

After this is ran against a loop, a library developer can run
the loop in its own thread and then use the returned
`high_level_loop` to interact with it.

# Arguments

* loop_ptr - a pointer to a newly created `uv_loop_t*` with no
handles registered (this will interfere with the internal lifecycle
management this module provides). Ideally, this should be called
immediately after using `uv::ll::loop_new()`

# Return

A `high_level_loop` record that can be used to interact with the
loop (after you use `uv::ll::run()` on the `uv_loop_t*`, of course
"]
unsafe fn prepare_loop(loop_ptr: *libc::c_void)
    -> high_level_loop {
    // will probably need to stake out a data record
    // here, as well, to keep whatever state we want to
    // use with the loop

    // move this into a malloc
    let async = ll::async_t();
    let async_ptr = ptr::addr_of(async);
    let op_port = comm::port::<fn~(*libc::c_void)>();
    let async_result = ll::async_init(loop_ptr,
                                      async_ptr,
                                      interact_poke);
    if (async_result != 0i32) {
        fail ll::get_last_err_info(loop_ptr);
    }
    // need to store the port and async_ptr in the top-level
    // of the provided loop ..
    ret { async_handle: async_ptr,
         op_chan: comm::chan::<fn~(*libc::c_void)>(op_port)
        };
}

// this will be invoked by a called to uv::hl::interact(), so
// we'll drain the port of pending callbacks, processing each
crust fn interact_poke(async_handle: *libc::c_void) {
    // nothing here, yet.
    log(debug, #fmt("interact_poke crust.. handle: %?",
                     async_handle));
}<commit_msg>end-to-end impl of global loop w/ high-level ref counting.. needs work<commit_after>#[doc = "
High-level bindings to work with the libuv library.

This module is geared towards library developers who want to
provide a high-level, abstracted interface to some set of
libuv functionality.
"];

export high_level_loop;
export run_high_level_loop, interact, ref_handle, unref_handle;
// this will eventually move into its own, unexported (from std) module
export get_global_loop;

import ll = uv_ll;

native mod rustrt {
    fn rust_uv_get_kernel_global_chan_ptr() -> *libc::uintptr_t;
    fn rust_uv_get_kernel_global_async_handle() -> **libc::c_void;
    fn rust_uv_set_kernel_global_async_handle(handle: *ll::uv_async_t);
    fn rust_uv_free_kernel_global_async_handle();
}

#[doc = "
Used to abstract-away direct interaction with a libuv loop.

# Arguments

* async_handle - a pointer to a pointer to a uv_async_t struct used to 'poke'
the C uv loop to process any pending callbacks

* op_chan - a channel used to send function callbacks to be processed
by the C uv loop
"]
type high_level_loop = {
    async_handle: **ll::uv_async_t,
    op_chan: comm::chan<high_level_msg>
};

#[doc = "
Race-free helper to get access to a global task where a libuv
loop is running.

# Return

* A `high_level_loop` that encapsulates communication with the global loop.
"]
fn get_global_loop() -> high_level_loop unsafe {
    let global_loop_chan_ptr = rustrt::rust_uv_get_kernel_global_chan_ptr();
    log(debug, #fmt("ENTERING get_global_loop() loop chan: %?",
       global_loop_chan_ptr));
    log(debug, #fmt("ENTERING get_global_loop() loop chan: %?",
       global_loop_chan_ptr));
    log(debug,#fmt("value of loop ptr: %?", *global_loop_chan_ptr));

    let builder_fn = {||
        let builder = task::builder();
        let opts = {
            supervise: true,
            notify_chan: none,
            sched:
                some({mode: task::manual_threads(1u),
                      native_stack_size: none })
        };
        task::set_opts(builder, opts);
        builder
    };
    unsafe {
        log(debug, "before priv::chan_from_global_ptr");
        let chan = priv::chan_from_global_ptr::<high_level_msg>(
            global_loop_chan_ptr,
            builder_fn) {|port|

            // the actual body of our global loop lives here
            log(debug, "initialized global port task!");
            log(debug, "GLOBAL!!!! initialized global port task!");
            outer_global_loop_body(port);
        };
        log(debug, "after priv::chan_from_global_ptr");
        let handle = get_global_async_handle();
        ret { async_handle: handle, op_chan: chan };
    }
}

#[doc = "
Takes a vanilla libuv `uv_loop_t*` ptr, performs some setup and then calls
`uv_run()`. Users will be able to access this loop via a provided
`async_handle` and `msg_ptr_po`. On top of libuv's internal handle refcount,
the high_level_loop manages its own lifetime with a similar refcount scheme.

This call blocks for the lifetime of the libuv loop.

# Arguments

* loop_ptr - a pointer to a currently unused libuv loop. Its `data` field
will be overwritten before the loop begins
* async_handle - a pointer to a _fresh_ `ll::uv_async_t` record that _has
not_ been initialized via `uv_async_init`, `ll::uv::async_init`, etc. It
must be a pointer to a clean rust `uv_async_t` record
* before_run - a unique closure that is invoked just before the call to
`uv_run`
* before_msg_drain - a unique closure that is invoked every time the loop is
awoken, but before the port pointed to in the `msg_po` argument is drained.
"]
unsafe fn run_high_level_loop(loop_ptr: *libc::c_void,
                              msg_po: comm::port<high_level_msg>,
                              before_run: fn~(*global_loop_data),
                              before_msg_drain: fn~() -> bool,
                              before_tear_down: fn~()) {
    // set up the special async handle we'll use to allow multi-task
    // communication with this loop
    let async = ll::async_t();
    let async_handle = ptr::addr_of(async);
    // associate the async handle with the loop
    ll::async_init(loop_ptr, async_handle, high_level_wake_up_cb);

    // initialize our loop data and store it in the loop
    let data: global_loop_data = {
        async_handle: async_handle,
        mut active: true,
        before_msg_drain: before_msg_drain,
        before_tear_down: before_tear_down,
        msg_po_ptr: ptr::addr_of(msg_po),
        mut refd_handles: [mut],
        mut unrefd_handles: [mut]
    };
    let data_ptr = ptr::addr_of(data);
    ll::set_data_for_uv_handle(async_handle, data_ptr);

    // call before_run
    before_run(data_ptr);

    log(debug, "about to run high level loop");
    // enter the loop... this blocks until the loop is done..
    ll::run(loop_ptr);
    log(debug, "high-level loop ended");
}

#[doc = "
Pass in a callback to be processed on the running libuv loop's thread

# Arguments

* a_loop - a high_level_loop record that represents a channel of
communication with an active libuv loop running on a thread
somwhere in the current process
* cb - a function callback to be processed on the running loop's
thread. The only parameter is an opaque pointer to the running
uv_loop_t. You can use this pointer to initiate or continue any
operations against the loop
"]
unsafe fn interact(a_loop: high_level_loop,
                      -cb: fn~(*libc::c_void)) {
    send_high_level_msg(a_loop, interaction(cb));
}

iface uv_handle_manager<T> {
    fn init() -> T;
}

resource uv_safe_handle<T>(handle_val: uv_handle_manager<T>) {
}

#[doc="
"]
fn ref_handle<T>(hl_loop: high_level_loop, handle: *T) unsafe {
    send_high_level_msg(hl_loop, auto_ref_handle(handle as *libc::c_void));
}
#[doc="
"]
fn unref_handle<T>(hl_loop: high_level_loop, handle: *T) unsafe {
    send_high_level_msg(hl_loop, auto_unref_handle(handle as *libc::c_void));
}

/////////////////////
// INTERNAL API
/////////////////////

unsafe fn send_high_level_msg(hl_loop: high_level_loop,
                              -msg: high_level_msg) unsafe {
    comm::send(hl_loop.op_chan, msg);

    // if the global async handle == 0, then that means
    // the loop isn't active, so we don't need to wake it up,
    // (the loop's enclosing task should be blocking on a message
    // receive on this port)
    if (*(hl_loop.async_handle) != 0 as *ll::uv_async_t) {
        log(debug,"global async handle != 0, waking up loop..");
        ll::async_send(*(hl_loop.async_handle));
    }
    else {
        log(debug,"GLOBAL ASYNC handle == 0");
    }
}

// this will be invoked by a call to uv::hl::interact() with
// the high_level_loop corresponding to this async_handle. We
// simply check if the loop is active and, if so, invoke the
// user-supplied on_wake callback that is stored in the loop's
// data member
crust fn high_level_wake_up_cb(async_handle: *libc::c_void,
                               status: int) unsafe {
    // nothing here, yet.
    log(debug, #fmt("high_level_wake_up_cb crust.. handle: %?",
                     async_handle));
    let loop_ptr = ll::get_loop_for_uv_handle(async_handle);
    let data = ll::get_data_for_uv_handle(async_handle) as *global_loop_data;
    // we check to see if the loop is "active" (the loop is set to
    // active = false the first time we realize we need to 'tear down',
    // set subsequent calls to the global async handle may be triggered
    // before all of the uv_close() calls are processed and loop exits
    // on its own. So if the loop isn't active, we won't run the user's
    // on_wake callback (and, consequently, let messages pile up, probably
    // in the loops msg_po)
    if (*data).active {
        log(debug, "before on_wake");
        let mut do_msg_drain = (*data).before_msg_drain();
        let mut continue = true;
        if do_msg_drain {
            let msg_po = *((*data).msg_po_ptr);
            if comm::peek(msg_po) {
                // if this is true, we'll iterate over the
                // msgs waiting in msg_po until there's no more
                log(debug,"got msg_po");
                while(continue) {
                    log(debug,"before alt'ing on high_level_msg");
                    alt comm::recv(msg_po) {
                      interaction(cb) {
                        log(debug,"got interaction, before cb..");
                        // call it..
                        cb(loop_ptr);
                        log(debug,"after calling cb");
                      }
                      auto_ref_handle(handle) {
                        high_level_ref(data, handle);
                      }
                      auto_unref_handle(handle) {
                        high_level_unref(data, handle, false);
                      }
                      tear_down {
                        log(debug,"incoming hl_msg: got tear_down");
                      }
                    }
                    continue = comm::peek(msg_po);
                }
            }
        }
        log(debug, #fmt("after on_wake, continue? %?", continue));
        if !do_msg_drain {
            high_level_tear_down(data);
        }
    }
}

crust fn tear_down_close_cb(handle: *ll::uv_async_t) unsafe {
    log(debug, #fmt("tear_down_close_cb called, closing handle at %?",
                    handle));
    // TODO: iterate through open handles on the loop and uv_close()
    // them all
    let data = ll::get_data_for_uv_handle(handle) as *global_loop_data;
}

fn high_level_tear_down(data: *global_loop_data) unsafe {
    log(debug, "high_level_tear_down() called, close async_handle");
    // call user-suppled before_tear_down cb
    (*data).before_tear_down();
    let async_handle = (*data).async_handle;
    ll::close(async_handle as *libc::c_void, tear_down_close_cb);
}

unsafe fn high_level_ref(data: *global_loop_data, handle: *libc::c_void) {
    log(debug,"incoming hl_msg: got auto_ref_handle");
    let mut refd_handles = (*data).refd_handles;
    let handle_already_refd = refd_handles.contains(handle);
    if handle_already_refd {
        fail "attempt to do a high-level ref an already ref'd handle";
    }
    refd_handles += [handle];
    (*data).refd_handles = refd_handles;
}

crust fn auto_unref_close_cb(handle: *libc::c_void) {
    log(debug, "closing handle via high_level_unref");
}

unsafe fn high_level_unref(data: *global_loop_data, handle: *libc::c_void,
                   manual_unref: bool) {
    log(debug,"incoming hl_msg: got auto_unref_handle");
    let mut refd_handles = (*data).refd_handles;
    let mut unrefd_handles = (*data).unrefd_handles;
    let handle_already_refd = refd_handles.contains(handle);
    if !handle_already_refd {
        fail "attempting to high-level unref an untracked handle";
    }
    let double_unref = unrefd_handles.contains(handle);
    if double_unref {
        if manual_unref {
            // will allow a user to manual unref, but only signal
            // a fail when a double-unref is caused by a user
            fail "attempting to high-level unref an unrefd handle";
        }
    }
    else {
        ll::close(handle, auto_unref_close_cb);
        let last_idx = vec::len(refd_handles) - 1u;
        let handle_idx = vec::position_elem(refd_handles, handle);
        alt handle_idx {
          none {
            fail "trying to remove handle that isn't in refd_handles";
          }
          some(idx) {
            refd_handles[idx] <-> refd_handles[last_idx];
            vec::pop(refd_handles);
          }
        }
        (*data).refd_handles = refd_handles;
        unrefd_handles += [handle];
        (*data).unrefd_handles = unrefd_handles;
        if vec::len(refd_handles) == 0u {
            log(debug, "0 referenced handles, start loop teardown");
            high_level_tear_down(data);
        }
        else {
            log(debug, "more than 0 referenced handles");
        }
    }

}

enum high_level_msg {
    interaction (fn~(*libc::c_void)),
    auto_ref_handle (*libc::c_void),
    auto_unref_handle (*libc::c_void),
    tear_down
}

fn get_global_async_handle() -> **ll::uv_async_t {
    ret rustrt::rust_uv_get_kernel_global_async_handle() as **ll::uv_async_t;
}

fn set_global_async_handle(handle: *ll::uv_async_t) {
    rustrt::rust_uv_set_kernel_global_async_handle(handle);
}

type global_loop_data = {
    async_handle: *ll::uv_async_t,
    mut active: bool,
    before_msg_drain: fn~() -> bool,
    before_tear_down: fn~(),
    msg_po_ptr: *comm::port<high_level_msg>,
    mut refd_handles: [mut *libc::c_void],
    mut unrefd_handles: [mut *libc::c_void]
};

unsafe fn outer_global_loop_body(msg_po: comm::port<high_level_msg>) {
    // we're going to use a single libuv-generated loop ptr
    // for the duration of the process
    let loop_ptr = ll::loop_new();

    // data structure for loop goes here..

    // immediately weaken the task this is running in.
    priv::weaken_task() {|weak_exit_po|
        // when we first enter this loop, we're going
        // to wait on stand-by to receive a request to
        // fire-up the libuv loop
        let mut continue = true;
        while continue {
            log(debug, "in outer_loop...");
            continue = either::either(
                {|left_val|
                    // bail out..
                    // if we catch this msg at this point,
                    // we should just be able to exit because
                    // the loop isn't active
                    log(debug, "got msg on weak_exit_po in outer loop");
                    false
                }, {|right_val|
                    log(debug, "about to enter inner loop");
                    inner_global_loop_body(weak_exit_po, msg_po, loop_ptr,
                                          copy(right_val))
                }, comm::select2(weak_exit_po, msg_po));
            log(debug,#fmt("GLOBAL LOOP EXITED, WAITING TO RESTART? %?",
                       continue));
        }
    };

    ll::loop_delete(loop_ptr);
    // once we get here, show's over.
    rustrt::rust_uv_free_kernel_global_async_handle();
}

unsafe fn inner_global_loop_body(weak_exit_po_in: comm::port<()>,
                          msg_po_in: comm::port<high_level_msg>,
                          loop_ptr: *libc::c_void,
                          -first_interaction: high_level_msg) -> bool {
    // resend the msg
    comm::send(comm::chan(msg_po_in), first_interaction);

    // black magic
    let weak_exit_po_ptr = ptr::addr_of(weak_exit_po_in);
    run_high_level_loop(
        loop_ptr,
        msg_po_in,
        // before_run
        {|data|
            // set the handle as the global
            set_global_async_handle((*data).async_handle);
            // when this is ran, our async_handle is set up, so let's
            // do an async_send with it
            ll::async_send((*data).async_handle);
        },
        // before_msg_drain
        {||
            log(debug,"entering before_msg_drain for the global loop");
            let weak_exit_po = *weak_exit_po_ptr;
            if(comm::peek(weak_exit_po)) {
                // if this is true, immediately bail and return false, causing
                // the libuv loop to start tearing down
                log(debug,"got weak_exit meg inside libuv loop");
                comm::recv(weak_exit_po);
                false
            }
            // if no weak_exit_po msg is received, then we'll let the
            // loop continue
            else {
                true
            }
        },
        // before_tear_down
        {||
            set_global_async_handle(0 as *ll::uv_async_t);
        });
    // supposed to return a bool to indicate to the enclosing loop whether
    // it should continue or not..
    ret true;
}

#[cfg(test)]
mod test {
    crust fn simple_timer_close_cb(timer_ptr: *ll::uv_timer_t) unsafe {
        log(debug, "UNUSED...");
    }
    crust fn simple_timer_cb(timer_ptr: *ll::uv_timer_t,
                             status: libc::c_int) unsafe {
        log(debug, "in simple timer cb");
        let exit_ch_ptr = ll::get_data_for_uv_handle(
            timer_ptr as *libc::c_void) as *comm::chan<bool>;
        ll::timer_stop(timer_ptr);
        let hl_loop = get_global_loop();
        interact(hl_loop) {|loop_ptr|
            log(debug, "closing timer");
            //ll::close(timer_ptr as *libc::c_void, simple_timer_close_cb);
            unref_handle(hl_loop, timer_ptr);
            log(debug, "about to deref exit_ch_ptr");
            let exit_ch = *exit_ch_ptr;
            comm::send(exit_ch, true);
            log(debug, "after msg sent on deref'd exit_ch");
        };
        log(debug, "exiting simple timer cb");
    }
    #[test]
    fn test_uv_hl_simple_timer() unsafe {
        let exit_po = comm::port::<bool>();
        let exit_ch = comm::chan(exit_po);
        let exit_ch_ptr = ptr::addr_of(exit_ch);
        let hl_loop = get_global_loop();
        let timer_handle = ll::timer_t();
        let timer_ptr = ptr::addr_of(timer_handle);
        interact(hl_loop) {|loop_ptr|
            log(debug, "user code inside interact loop!!!");
            let init_status = ll::timer_init(loop_ptr, timer_ptr);
            if(init_status == 0i32) {
                ref_handle(hl_loop, timer_ptr);
                ll::set_data_for_uv_handle(
                    timer_ptr as *libc::c_void,
                    exit_ch_ptr as *libc::c_void);
                let start_status = ll::timer_start(timer_ptr, simple_timer_cb,
                                                   1u, 0u);
                if(start_status == 0i32) {
                }
                else {
                    fail "failure on ll::timer_start()";
                }
            }
            else {
                fail "failure on ll::timer_init()";
            }
        };
        comm::recv(exit_po);
        log(debug, "test_uv_hl_simple_timer: msg recv on exit_po, done..");
    }
}<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::future::Future;
use std::sync::Mutex;

use http::status::StatusCode;

use serde::{Deserialize, Serialize};

use tide::{prelude::*, IntoResponse, Request, Response, Result, ResultExt, Server};

#[derive(Default)]
struct Database {
    contents: Mutex<HashMap<usize, Message>>
}

#[derive(Serialize, Deserialize, Clone)]
struct Message {
    author: Option<String>,
    contents: String
}

impl Database {
    fn set(&self, id: usize, msg: Message) {
        let mut contents = self.contents.lock().unwrap();

        if contents.contains_key(&id) {
            contents.entry(id).and_modify(|v| *v = msg);
            return
        }

        contents.insert(id, msg);
    }

    fn get(&self, id: usize) -> Option<Message> {
        let mut contents = self.contents.lock().unwrap();

        match contents.get(&id) {
            Some(v) => Some(v.clone()),
            None => None
        }
    }
}

async fn handle_post(mut req: Request<Database>) -> Response {
    let mut msg = match req.body_json().await.client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) =>  return e.into_response()
    };

    req.state().set(id, msg);
    Response::new(StatusCode::OK.into()).body_string("".to_owned())
}

async fn handle_get(mut req: Request<Database>) -> Response {
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };

    if let Some(msg) = req.state().get(id) {
        match Response::new(StatusCode::OK.into()).body_json(&msg) {
            Ok(v) => v,
            _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())

        }
    }
    else {
        Response::new(StatusCode::NOT_FOUND.into())
    }
}

#[async_std::main]
async fn main() -> std::io::Result<()> {
    let mut app = Server::with_state(Database::default());
    app.at("/").get(|_| async move { "Hello, world!" });
    app.at("/message/:id").post(handle_post).get(handle_get);
    app.listen("127.0.0.1:8080").await?;
    Ok(())
}
<commit_msg>Rust_Tide: adding static file serving<commit_after>use std::collections::HashMap;
use std::future::Future;
use std::sync::{Mutex, Arc};

use async_std::{fs, path::{Path, PathBuf}};

use http::status::StatusCode;

use serde::{Deserialize, Serialize};

use tide::{prelude::*, IntoResponse, Request, Response, ResultExt, Server};

struct Database {
    contents: Mutex<HashMap<usize, Message>>,
    static_file_server: Mutex<StaticFileHandler>
}

#[derive(Serialize, Deserialize, Clone)]
struct Message {
    author: Option<String>,
    contents: String
}

impl Database {
    async fn new(root_path: &str) -> std::io::Result<Database> {
        Ok(Database {
            contents: Default::default(),
            static_file_server: Mutex::new(StaticFileHandler::new(root_path).await?)
        })
    }

    fn set(&self, id: usize, msg: Message) {
        let mut contents = self.contents.lock().unwrap();

        if contents.contains_key(&id) {
            contents.entry(id).and_modify(|v| *v = msg);
            return
        }

        contents.insert(id, msg);
    }

    fn get(&self, id: usize) -> Option<Message> {
        let mut contents = self.contents.lock().unwrap();

        match contents.get(&id) {
            Some(v) => Some(v.clone()),
            None => None
        }
    }
}

struct StaticFileHandler {
    root_path: PathBuf,
    file_cache: HashMap<PathBuf, Arc<Mutex<String>>>
}

impl StaticFileHandler {
    async fn new(root_path: &str) -> std::io::Result<StaticFileHandler> {
        let abs_root_path = fs::canonicalize(root_path).await?;

        Ok(StaticFileHandler {
            root_path : abs_root_path,
            file_cache : Default::default()
        })
    }

    async fn check_valid_path(&self, input_path: &Path) -> std::io::Result<bool> {
        let abs_input_path = fs::canonicalize(input_path).await?;

        Ok(abs_input_path.as_path().starts_with(&self.root_path))
    }

    async fn cache_file(&mut self, input_path: &Path) -> std::io::Result<bool> {
        debug_assert!(self.check_valid_path(input_path).await?);

        // 1. load file
        let new_file_data = Arc::new(Mutex::new(fs::read_to_string(input_path).await?));

        // 2. build key
        let abs_input_path = fs::canonicalize(input_path).await?;

        // 3. set key value
        if self.file_cache.contains_key(&abs_input_path) {
            self.file_cache.entry(abs_input_path).and_modify(|e| *e = new_file_data);
        }
        else {
            self.file_cache.insert(abs_input_path, new_file_data);
        }

        Ok(true)
    }

    async fn get_cached_file(&self, input_path: &Path) -> std::io::Result<Arc<Mutex<String>>> {
        if !self.check_valid_path(input_path).await? {
            return Err(std::io::Error::from(std::io::ErrorKind::NotFound))
        }

        let abs_input_path = fs::canonicalize(input_path).await?;

        if !self.file_cache.contains_key(&abs_input_path) {
            self.cache_file(&abs_input_path).await?;
        }

        match self.file_cache.get(&abs_input_path) {
            Some(v) => Ok(v.clone()),
            None => Err(std::io::Error::from(std::io::ErrorKind::NotFound))
        }
    }
}

async fn handle_post(mut req: Request<Database>) -> Response {
    let mut msg = match req.body_json().await.client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) =>  return e.into_response()
    };

    req.state().set(id, msg);
    Response::new(StatusCode::OK.into()).body_string("".to_owned())
}

async fn handle_get(mut req: Request<Database>) -> Response {
    let id = match req.param("id").client_err() {
        Ok(v) => v,
        Err(e) => return e.into_response()
    };

    if let Some(msg) = req.state().get(id) {
        match Response::new(StatusCode::OK.into()).body_json(&msg) {
            Ok(v) => v,
            _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())

        }
    }
    else {
        Response::new(StatusCode::NOT_FOUND.into())
    }
}

async fn handle_get_static(mut req: Request<Database>) -> Response {
    let rel_path_buf = req.uri().path().to_owned();
    let rel_path = Path::new(&rel_path_buf[7..]);

    let mut sfs = req.state().static_file_server.lock().unwrap();

    match sfs.get_cached_file(&rel_path).await {
        //Ok(v) => Response::new(StatusCode::OK.into()).body_string(*v),
        Ok(v) => Response::new(StatusCode::OK.into()),
        _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR.into())
    }
}

#[async_std::main]
async fn main() -> std::io::Result<()> {
    let database = Database::new("./static_files/").await?;

    let mut app = Server::with_state(database);
    app.at("/").get(|_| async move { "Hello, world!" });
    app.at("/message/:id").post(handle_post).get(handle_get);
    app.at("/static/*").get(handle_get_static);
    app.listen("127.0.0.1:8080").await?;
    Ok(())
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::env;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use lazy_static::lazy_static;
use log::*;
use users::UsersCache;

use crate::fs::{File, fields as f};
use crate::fs::feature::git::GitCache;
use crate::output::cell::TextCell;
use crate::output::render::TimeRender;
use crate::output::time::TimeFormat;
use crate::theme::Theme;

/// Options for displaying a table.
#[derive(PartialEq, Debug)]
pub struct Options {
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub columns: Columns,
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,
    pub octal: bool,

    // Defaults to true:
    pub permissions: bool,
    pub filesize: bool,
    pub user: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        if self.octal {
            columns.push(Column::Octal);
        }

        if self.permissions {
            columns.push(Column::Permissions);
        }

        if self.links {
            columns.push(Column::HardLinks);
        }

        if self.filesize {
            columns.push(Column::FileSize);
        }

        if self.blocks {
            columns.push(Column::Blocks);
        }

        if self.user {
            columns.push(Column::User);
        }

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.changed {
            columns.push(Column::Timestamp(TimeType::Changed));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature = "git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug, Copy, Clone)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
    Octal,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left,
    Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(self) -> Alignment {
        match self {
            Self::FileSize   |
            Self::HardLinks  |
            Self::Inode      |
            Self::Blocks     |
            Self::GitStatus  => Alignment::Right,
            _                => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(self) -> &'static str {
        match self {
            Self::Permissions   => "Permissions",
            Self::FileSize      => "Size",
            Self::Timestamp(t)  => t.header(),
            Self::Blocks        => "Blocks",
            Self::User          => "User",
            Self::Group         => "Group",
            Self::HardLinks     => "Links",
            Self::Inode         => "inode",
            Self::GitStatus     => "Git",
            Self::Octal         => "Octal",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> Self {
        Self::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {

    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s changed time (`st_ctime`)
    Changed,

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s creation time (`btime` or `birthtime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(self) -> &'static str {
        match self {
            Self::Modified  => "Date Modified",
            Self::Changed   => "Date Changed",
            Self::Accessed  => "Date Accessed",
            Self::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these — there’s no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct TimeTypes {
    pub modified: bool,
    pub changed:  bool,
    pub accessed: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> Self {
        Self {
            modified: true,
            changed:  false,
            accessed: false,
            created:  false,
        }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user’s computer’s configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer’s current time zone. This gets used to determine how to
    /// offset files’ timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> {
        self.users.lock().unwrap()
    }

    fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => {
                Some(t)
            }
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                             .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Self { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    if let Ok(file) = env::var("TZ") {
        TimeZone::from_file({
            if file.starts_with("/") {
                file
            } else {
                format!("/usr/share/zoneinfo/{}", {
                    if file.starts_with(":") {
                        // Unwrap is panic-free here because we've checked if file starts with colon
                        file.strip_prefix(":").unwrap()
                    } else {
                        file.as_str()
                    }
                })
            }
        })
    } else {
        TimeZone::from_file("/etc/localtime")
    }
}

lazy_static! {
    static ref ENVIRONMENT: Environment = Environment::load_all();
}

pub struct Table<'a> {
    columns: Vec<Column>,
    theme: &'a Theme,
    env: &'a Environment,
    widths: TableWidths,
    time_format: TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, git: Option<&'a GitCache>, theme: &'a Theme) -> Table<'a> {
        let columns = options.columns.collect(git.is_some());
        let widths = TableWidths::zero(columns.len());
        let env = &*ENVIRONMENT;

        Table {
            theme,
            widths,
            columns,
            git,
            env,
            time_format: options.time_format,
            size_format: options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.theme.ui.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File<'_>, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, *c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs,
        }
    }

    fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
        f::OctalPermissions {
            permissions: file.permissions(),
        }
    }

    fn display(&self, file: &File<'_>, column: Column, xattrs: bool) -> TextCell {
        match column {
            Column::Permissions => {
                self.permissions_plus(file, xattrs).render(self.theme)
            }
            Column::FileSize => {
                file.size().render(self.theme, self.size_format, &self.env.numeric)
            }
            Column::HardLinks => {
                file.links().render(self.theme, &self.env.numeric)
            }
            Column::Inode => {
                file.inode().render(self.theme.ui.inode)
            }
            Column::Blocks => {
                file.blocks().render(self.theme)
            }
            Column::User => {
                file.user().render(self.theme, &*self.env.lock_users())
            }
            Column::Group => {
                file.group().render(self.theme, &*self.env.lock_users())
            }
            Column::GitStatus => {
                self.git_status(file).render(self.theme)
            }
            Column::Octal => {
                self.octal_permissions(file).render(self.theme.ui.octal)
            }

            Column::Timestamp(TimeType::Modified)  => {
                file.modified_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
            Column::Timestamp(TimeType::Changed)   => {
                file.changed_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
            Column::Timestamp(TimeType::Created)   => {
                file.created_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
            Column::Timestamp(TimeType::Accessed)  => {
                file.accessed_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
        }
    }

    fn git_status(&self, file: &File<'_>) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);

        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        let iter = row.cells.into_iter()
                      .zip(self.widths.iter())
                      .enumerate();

        for (n, (this_cell, width)) in iter {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left => {
                    cell.append(this_cell);
                    cell.add_spaces(padding);
                }
                Alignment::Right => {
                    cell.add_spaces(padding);
                    cell.append(this_cell);
                }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> Self {
        Self(vec![0; count])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<commit_msg>Replace strip_prefix with replace<commit_after>use std::cmp::max;
use std::env;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use lazy_static::lazy_static;
use log::*;
use users::UsersCache;

use crate::fs::{File, fields as f};
use crate::fs::feature::git::GitCache;
use crate::output::cell::TextCell;
use crate::output::render::TimeRender;
use crate::output::time::TimeFormat;
use crate::theme::Theme;

/// Options for displaying a table.
#[derive(PartialEq, Debug)]
pub struct Options {
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub columns: Columns,
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,
    pub octal: bool,

    // Defaults to true:
    pub permissions: bool,
    pub filesize: bool,
    pub user: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        if self.octal {
            columns.push(Column::Octal);
        }

        if self.permissions {
            columns.push(Column::Permissions);
        }

        if self.links {
            columns.push(Column::HardLinks);
        }

        if self.filesize {
            columns.push(Column::FileSize);
        }

        if self.blocks {
            columns.push(Column::Blocks);
        }

        if self.user {
            columns.push(Column::User);
        }

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.changed {
            columns.push(Column::Timestamp(TimeType::Changed));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature = "git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug, Copy, Clone)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
    Octal,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left,
    Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(self) -> Alignment {
        match self {
            Self::FileSize   |
            Self::HardLinks  |
            Self::Inode      |
            Self::Blocks     |
            Self::GitStatus  => Alignment::Right,
            _                => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(self) -> &'static str {
        match self {
            Self::Permissions   => "Permissions",
            Self::FileSize      => "Size",
            Self::Timestamp(t)  => t.header(),
            Self::Blocks        => "Blocks",
            Self::User          => "User",
            Self::Group         => "Group",
            Self::HardLinks     => "Links",
            Self::Inode         => "inode",
            Self::GitStatus     => "Git",
            Self::Octal         => "Octal",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> Self {
        Self::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {

    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s changed time (`st_ctime`)
    Changed,

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s creation time (`btime` or `birthtime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(self) -> &'static str {
        match self {
            Self::Modified  => "Date Modified",
            Self::Changed   => "Date Changed",
            Self::Accessed  => "Date Accessed",
            Self::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these — there’s no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct TimeTypes {
    pub modified: bool,
    pub changed:  bool,
    pub accessed: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> Self {
        Self {
            modified: true,
            changed:  false,
            accessed: false,
            created:  false,
        }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user’s computer’s configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer’s current time zone. This gets used to determine how to
    /// offset files’ timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> {
        self.users.lock().unwrap()
    }

    fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => {
                Some(t)
            }
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                             .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Self { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    if let Ok(file) = env::var("TZ") {
        TimeZone::from_file({
            if file.starts_with("/") {
                file
            } else {
                format!("/usr/share/zoneinfo/{}", {
                    if file.starts_with(":") {
                        file.replace(":", "")
                    } else {
                        file
                    }
                })
            }
        })
    } else {
        TimeZone::from_file("/etc/localtime")
    }
}

lazy_static! {
    static ref ENVIRONMENT: Environment = Environment::load_all();
}

pub struct Table<'a> {
    columns: Vec<Column>,
    theme: &'a Theme,
    env: &'a Environment,
    widths: TableWidths,
    time_format: TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, git: Option<&'a GitCache>, theme: &'a Theme) -> Table<'a> {
        let columns = options.columns.collect(git.is_some());
        let widths = TableWidths::zero(columns.len());
        let env = &*ENVIRONMENT;

        Table {
            theme,
            widths,
            columns,
            git,
            env,
            time_format: options.time_format,
            size_format: options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.theme.ui.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File<'_>, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, *c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs,
        }
    }

    fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
        f::OctalPermissions {
            permissions: file.permissions(),
        }
    }

    fn display(&self, file: &File<'_>, column: Column, xattrs: bool) -> TextCell {
        match column {
            Column::Permissions => {
                self.permissions_plus(file, xattrs).render(self.theme)
            }
            Column::FileSize => {
                file.size().render(self.theme, self.size_format, &self.env.numeric)
            }
            Column::HardLinks => {
                file.links().render(self.theme, &self.env.numeric)
            }
            Column::Inode => {
                file.inode().render(self.theme.ui.inode)
            }
            Column::Blocks => {
                file.blocks().render(self.theme)
            }
            Column::User => {
                file.user().render(self.theme, &*self.env.lock_users())
            }
            Column::Group => {
                file.group().render(self.theme, &*self.env.lock_users())
            }
            Column::GitStatus => {
                self.git_status(file).render(self.theme)
            }
            Column::Octal => {
                self.octal_permissions(file).render(self.theme.ui.octal)
            }

            Column::Timestamp(TimeType::Modified)  => {
                file.modified_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
            Column::Timestamp(TimeType::Changed)   => {
                file.changed_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
            Column::Timestamp(TimeType::Created)   => {
                file.created_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
            Column::Timestamp(TimeType::Accessed)  => {
                file.accessed_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
            }
        }
    }

    fn git_status(&self, file: &File<'_>) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);

        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        let iter = row.cells.into_iter()
                      .zip(self.widths.iter())
                      .enumerate();

        for (n, (this_cell, width)) in iter {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left => {
                    cell.append(this_cell);
                    cell.add_spaces(padding);
                }
                Alignment::Right => {
                    cell.add_spaces(padding);
                    cell.append(this_cell);
                }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> Self {
        Self(vec![0; count])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<|endoftext|>


<commit_before>//! Abstractions for reading and modifying the mapping of pages.

pub use self::mapped_page_table::{MappedPageTable, PhysToVirt};
#[cfg(target_arch = "x86_64")]
pub use self::offset_page_table::OffsetPageTable;
#[cfg(target_arch = "x86_64")]
pub use self::recursive_page_table::RecursivePageTable;

use crate::structures::paging::{
    frame_alloc::FrameAllocator, page_table::PageTableFlags, Page, PageSize, PhysFrame, Size1GiB,
    Size2MiB, Size4KiB,
};
use crate::{PhysAddr, VirtAddr};

mod mapped_page_table;
mod offset_page_table;
mod recursive_page_table;

/// This trait defines page table operations that work for all page sizes of the x86_64
/// architecture.
pub trait MapperAllSizes: Mapper<Size4KiB> + Mapper<Size2MiB> + Mapper<Size1GiB> {
    /// Return the frame that the given virtual address is mapped to and the offset within that
    /// frame.
    ///
    /// If the given address has a valid mapping, the mapped frame and the offset within that
    /// frame is returned. Otherwise an error value is returned.
    ///
    /// This function works with huge pages of all sizes.
    fn translate(&self, addr: VirtAddr) -> TranslateResult;

    /// Translates the given virtual address to the physical address that it maps to.
    ///
    /// Returns `None` if there is no valid mapping for the given address.
    ///
    /// This is a convenience method. For more information about a mapping see the
    /// [`translate`](MapperAllSizes::translate) method.
    fn translate_addr(&self, addr: VirtAddr) -> Option<PhysAddr> {
        match self.translate(addr) {
            TranslateResult::PageNotMapped | TranslateResult::InvalidFrameAddress(_) => None,
            TranslateResult::Frame4KiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame2MiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame1GiB { frame, offset } => Some(frame.start_address() + offset),
        }
    }
}

/// The return value of the [`MapperAllSizes::translate`] function.
///
/// If the given address has a valid mapping, a `Frame4KiB`, `Frame2MiB`, or `Frame1GiB` variant
/// is returned, depending on the size of the mapped page. The remaining variants indicate errors.
#[derive(Debug)]
pub enum TranslateResult {
    /// The page is mapped to a physical frame of size 4KiB.
    Frame4KiB {
        /// The mapped frame.
        frame: PhysFrame<Size4KiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame2MiB {
        /// The mapped frame.
        frame: PhysFrame<Size2MiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame1GiB {
        /// The mapped frame.
        frame: PhysFrame<Size1GiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// A trait for common page table operations on pages of size `S`.
pub trait Mapper<S: PageSize> {
    /// Creates a new mapping in the page table.
    ///
    /// This function might need additional physical frames to create new page tables. These
    /// frames are allocated from the `allocator` argument. At most three frames are required.
    ///
    /// This function is unsafe because the caller must guarantee that passed `frame` is
    /// unused, i.e. not used for any other mappings.
    unsafe fn map_to<A>(
        &mut self,
        page: Page<S>,
        frame: PhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        A: FrameAllocator<Size4KiB>;

    /// Removes a mapping from the page table and returns the frame that used to be mapped.
    ///
    /// Note that no page tables or pages are deallocated.
    fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;

    /// Updates the flags of an existing mapping.
    fn update_flags(
        &mut self,
        page: Page<S>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<S>, FlagUpdateError>;

    /// Return the frame that the specified page is mapped to.
    ///
    /// This function assumes that the page is mapped to a frame of size `S` and returns an
    /// error otherwise.
    fn translate_page(&self, page: Page<S>) -> Result<PhysFrame<S>, TranslateError>;

    /// Maps the given frame to the virtual page with the same address.
    ///
    /// This function is unsafe because the caller must guarantee that the passed `frame` is
    /// unused, i.e. not used for any other mappings.
    unsafe fn identity_map<A>(
        &mut self,
        frame: PhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
        S: PageSize,
        Self: Mapper<S>,
    {
        let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
        self.map_to(page, frame, flags, frame_allocator)
    }
}

/// This type represents a page whose mapping has changed in the page table.
///
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
/// to be flushed from the TLB before it's accessed. This type is returned from function that
/// change the mapping of a page to ensure that the TLB flush is not forgotten.
#[derive(Debug)]
#[must_use = "Page Table changes must be flushed or ignored."]
pub struct MapperFlush<S: PageSize>(Page<S>);

impl<S: PageSize> MapperFlush<S> {
    /// Create a new flush promise
    fn new(page: Page<S>) -> Self {
        MapperFlush(page)
    }

    /// Flush the page from the TLB to ensure that the newest mapping is used.
    #[cfg(target_arch = "x86_64")]
    pub fn flush(self) {
        crate::instructions::tlb::flush(self.0.start_address());
    }

    /// Don't flush the TLB and silence the “must be used” warning.
    pub fn ignore(self) {}
}

/// This error is returned from `map_to` and similar methods.
#[derive(Debug)]
pub enum MapToError {
    /// An additional frame was needed for the mapping process, but the frame allocator
    /// returned `None`.
    FrameAllocationFailed,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of an already mapped huge page.
    ParentEntryHugePage,
    /// The given page is already mapped to a physical frame.
    PageAlreadyMapped,
}

/// An error indicating that an `unmap` call failed.
#[derive(Debug)]
pub enum UnmapError {
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// An error indicating that an `update_flags` call failed.
#[derive(Debug)]
pub enum FlagUpdateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
}

/// An error indicating that an `translate` call failed.
#[derive(Debug)]
pub enum TranslateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}
<commit_msg>Simplify reexports<commit_after>//! Abstractions for reading and modifying the mapping of pages.

pub use self::mapped_page_table::{MappedPageTable, PhysToVirt};
#[cfg(target_arch = "x86_64")]
pub use self::{
    offset_page_table::OffsetPageTable,
    recursive_page_table::RecursivePageTable,
};

use crate::structures::paging::{
    frame_alloc::FrameAllocator, page_table::PageTableFlags, Page, PageSize, PhysFrame, Size1GiB,
    Size2MiB, Size4KiB,
};
use crate::{PhysAddr, VirtAddr};

mod mapped_page_table;
mod offset_page_table;
mod recursive_page_table;

/// This trait defines page table operations that work for all page sizes of the x86_64
/// architecture.
pub trait MapperAllSizes: Mapper<Size4KiB> + Mapper<Size2MiB> + Mapper<Size1GiB> {
    /// Return the frame that the given virtual address is mapped to and the offset within that
    /// frame.
    ///
    /// If the given address has a valid mapping, the mapped frame and the offset within that
    /// frame is returned. Otherwise an error value is returned.
    ///
    /// This function works with huge pages of all sizes.
    fn translate(&self, addr: VirtAddr) -> TranslateResult;

    /// Translates the given virtual address to the physical address that it maps to.
    ///
    /// Returns `None` if there is no valid mapping for the given address.
    ///
    /// This is a convenience method. For more information about a mapping see the
    /// [`translate`](MapperAllSizes::translate) method.
    fn translate_addr(&self, addr: VirtAddr) -> Option<PhysAddr> {
        match self.translate(addr) {
            TranslateResult::PageNotMapped | TranslateResult::InvalidFrameAddress(_) => None,
            TranslateResult::Frame4KiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame2MiB { frame, offset } => Some(frame.start_address() + offset),
            TranslateResult::Frame1GiB { frame, offset } => Some(frame.start_address() + offset),
        }
    }
}

/// The return value of the [`MapperAllSizes::translate`] function.
///
/// If the given address has a valid mapping, a `Frame4KiB`, `Frame2MiB`, or `Frame1GiB` variant
/// is returned, depending on the size of the mapped page. The remaining variants indicate errors.
#[derive(Debug)]
pub enum TranslateResult {
    /// The page is mapped to a physical frame of size 4KiB.
    Frame4KiB {
        /// The mapped frame.
        frame: PhysFrame<Size4KiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame2MiB {
        /// The mapped frame.
        frame: PhysFrame<Size2MiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The page is mapped to a physical frame of size 2MiB.
    Frame1GiB {
        /// The mapped frame.
        frame: PhysFrame<Size1GiB>,
        /// The offset whithin the mapped frame.
        offset: u64,
    },
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// A trait for common page table operations on pages of size `S`.
pub trait Mapper<S: PageSize> {
    /// Creates a new mapping in the page table.
    ///
    /// This function might need additional physical frames to create new page tables. These
    /// frames are allocated from the `allocator` argument. At most three frames are required.
    ///
    /// This function is unsafe because the caller must guarantee that passed `frame` is
    /// unused, i.e. not used for any other mappings.
    unsafe fn map_to<A>(
        &mut self,
        page: Page<S>,
        frame: PhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        A: FrameAllocator<Size4KiB>;

    /// Removes a mapping from the page table and returns the frame that used to be mapped.
    ///
    /// Note that no page tables or pages are deallocated.
    fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>;

    /// Updates the flags of an existing mapping.
    fn update_flags(
        &mut self,
        page: Page<S>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<S>, FlagUpdateError>;

    /// Return the frame that the specified page is mapped to.
    ///
    /// This function assumes that the page is mapped to a frame of size `S` and returns an
    /// error otherwise.
    fn translate_page(&self, page: Page<S>) -> Result<PhysFrame<S>, TranslateError>;

    /// Maps the given frame to the virtual page with the same address.
    ///
    /// This function is unsafe because the caller must guarantee that the passed `frame` is
    /// unused, i.e. not used for any other mappings.
    unsafe fn identity_map<A>(
        &mut self,
        frame: PhysFrame<S>,
        flags: PageTableFlags,
        frame_allocator: &mut A,
    ) -> Result<MapperFlush<S>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
        S: PageSize,
        Self: Mapper<S>,
    {
        let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64()));
        self.map_to(page, frame, flags, frame_allocator)
    }
}

/// This type represents a page whose mapping has changed in the page table.
///
/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
/// to be flushed from the TLB before it's accessed. This type is returned from function that
/// change the mapping of a page to ensure that the TLB flush is not forgotten.
#[derive(Debug)]
#[must_use = "Page Table changes must be flushed or ignored."]
pub struct MapperFlush<S: PageSize>(Page<S>);

impl<S: PageSize> MapperFlush<S> {
    /// Create a new flush promise
    fn new(page: Page<S>) -> Self {
        MapperFlush(page)
    }

    /// Flush the page from the TLB to ensure that the newest mapping is used.
    #[cfg(target_arch = "x86_64")]
    pub fn flush(self) {
        crate::instructions::tlb::flush(self.0.start_address());
    }

    /// Don't flush the TLB and silence the “must be used” warning.
    pub fn ignore(self) {}
}

/// This error is returned from `map_to` and similar methods.
#[derive(Debug)]
pub enum MapToError {
    /// An additional frame was needed for the mapping process, but the frame allocator
    /// returned `None`.
    FrameAllocationFailed,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of an already mapped huge page.
    ParentEntryHugePage,
    /// The given page is already mapped to a physical frame.
    PageAlreadyMapped,
}

/// An error indicating that an `unmap` call failed.
#[derive(Debug)]
pub enum UnmapError {
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}

/// An error indicating that an `update_flags` call failed.
#[derive(Debug)]
pub enum FlagUpdateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
}

/// An error indicating that an `translate` call failed.
#[derive(Debug)]
pub enum TranslateError {
    /// The given page is not mapped to a physical frame.
    PageNotMapped,
    /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
    /// given page is part of a huge page and can't be freed individually.
    ParentEntryHugePage,
    /// The page table entry for the given page points to an invalid physical address.
    InvalidFrameAddress(PhysAddr),
}
<|endoftext|>