<commit_before>/*
Module: getopts

Simple getopt alternative. Construct a vector of options, either by using
reqopt, optopt, and optflag or by building them from components yourself, and
pass them to getopts, along with a vector of actual arguments (not including
argv[0]). You'll either get a failure code back, or a match.  You'll have to
verify whether the amount of 'free' arguments in the match is what you
expect. Use opt_* accessors to get argument values out of the match object.

Single-character options are expected to appear on the command line with a
single preceeding dash; multiple-character options are expected to be
proceeded by two dashes. Options that expect an argument accept their argument
following either a space or an equals sign.

Example:

The following example shows simple command line parsing for an application
that requires an input file to be specified, accepts an optional output file
name following -o, and accepts both -h and --help as optional flags.

> fn main(args: [str]) {
>   let opts = [
>     optopt("o"),
>     optflag("h"),
>     optflag("help")
>   ];
>   let match = alt getopts(vec::shift(args), opts) {
>     ok(m) { m }
>     err(f) { fail fail_str(f) }
>   };
>   if opt_present(match, "h") || opt_present(match, "help") {
>     print_usage();
>     ret;
>   }
>   let output = opt_maybe_str(match, "o");
>   let input = if !vec::is_empty(match.free) {
>     match.free[0]
>   } else {
>     print_usage();
>     ret;
>   }
>   do_work(input, output);
> }

*/

import core::result;
import core::result::{err, ok};
import core::option;
import core::option::{some, none};
export opt;
export reqopt;
export optopt;
export optflag;
export optflagopt;
export optmulti;
export getopts;
export match;
export fail_;
export fail_str;
export opt_present;
export opt_str;
export opt_strs;
export opt_maybe_str;
export opt_default;

tag name { long(str); short(char); }

tag hasarg { yes; no; maybe; }

tag occur { req; optional; multi; }

/*
Type: opt

A description of a possible option
*/
type opt = {name: name, hasarg: hasarg, occur: occur};

fn mkname(nm: str) -> name {
    ret if str::char_len(nm) == 1u {
            short(str::char_at(nm, 0u))
        } else { long(nm) };
}

/*
Function: reqopt

Create an option that is required and takes an argument
*/
fn reqopt(name: str) -> opt {
    ret {name: mkname(name), hasarg: yes, occur: req};
}

/*
Function: optopt

Create an option that is optional and takes an argument
*/
fn optopt(name: str) -> opt {
    ret {name: mkname(name), hasarg: yes, occur: optional};
}

/*
Function: optflag

Create an option that is optional and does not take an argument
*/
fn optflag(name: str) -> opt {
    ret {name: mkname(name), hasarg: no, occur: optional};
}

/*
Function: optflagopt

Create an option that is optional and takes an optional argument
*/
fn optflagopt(name: str) -> opt {
    ret {name: mkname(name), hasarg: maybe, occur: optional};
}

/*
Function: optmulti

Create an option that is optional, takes an argument, and may occur
multiple times
*/
fn optmulti(name: str) -> opt {
    ret {name: mkname(name), hasarg: yes, occur: multi};
}

tag optval { val(str); given; }

/*
Type: match

The result of checking command line arguments. Contains a vector
of matches and a vector of free strings.
*/
type match = {opts: [opt], vals: [mutable [optval]], free: [str]};

fn is_arg(arg: str) -> bool {
    ret str::byte_len(arg) > 1u && arg[0] == '-' as u8;
}

fn name_str(nm: name) -> str {
    ret alt nm { short(ch) { str::from_char(ch) } long(s) { s } };
}

fn find_opt(opts: [opt], nm: name) -> option::t<uint> {
    let i = 0u;
    let l = vec::len::<opt>(opts);
    while i < l { if opts[i].name == nm { ret some::<uint>(i); } i += 1u; }
    ret none::<uint>;
}

/*
Type: fail_

The type returned when the command line does not conform to the
expected format. Pass this value to <fail_str> to get an error message.
*/
tag fail_ {
    argument_missing(str);
    unrecognized_option(str);
    option_missing(str);
    option_duplicated(str);
    unexpected_argument(str);
}

/*
Function: fail_str

Convert a <fail_> tag into an error string
*/
fn fail_str(f: fail_) -> str {
    ret alt f {
          argument_missing(nm) { "Argument to option '" + nm + "' missing." }
          unrecognized_option(nm) { "Unrecognized option: '" + nm + "'." }
          option_missing(nm) { "Required option '" + nm + "' missing." }
          option_duplicated(nm) {
            "Option '" + nm + "' given more than once."
          }
          unexpected_argument(nm) {
            "Option " + nm + " does not take an argument."
          }
        };
}

/*
Type: result

The result of parsing a command line with a set of options
(result::t<match, fail_>)

Variants:

ok(match) - Returned from getopts on success
err(fail_) - Returned from getopts on failure
*/
type result = result::t<match, fail_>;

/*
Function: getopts

Parse command line arguments according to the provided options

Returns:

ok(match) - On success. Use functions such as <opt_present>
            <opt_str>, etc. to interrogate results.
err(fail_) - On failure. Use <fail_str> to get an error message.
*/
fn getopts(args: [str], opts: [opt]) -> result {
    let n_opts = vec::len::<opt>(opts);
    fn f(_x: uint) -> [optval] { ret []; }
    let vals = vec::init_fn_mut::<[optval]>(f, n_opts);
    let free: [str] = [];
    let l = vec::len(args);
    let i = 0u;
    while i < l {
        let cur = args[i];
        let curlen = str::byte_len(cur);
        if !is_arg(cur) {
            free += [cur];
        } else if str::eq(cur, "--") {
            let j = i + 1u;
            while j < l { free += [args[j]]; j += 1u; }
            break;
        } else {
            let names;
            let i_arg = option::none::<str>;
            if cur[1] == '-' as u8 {
                let tail = str::slice(cur, 2u, curlen);
                let eq = str::index(tail, '=' as u8);
                if eq == -1 {
                    names = [long(tail)];
                } else {
                    names = [long(str::slice(tail, 0u, eq as uint))];
                    i_arg =
                        option::some::<str>(str::slice(tail,
                                                       (eq as uint) + 1u,
                                                       curlen - 2u));
                }
            } else {
                let j = 1u;
                names = [];
                while j < curlen {
                    let range = str::char_range_at(cur, j);
                    names += [short(range.ch)];
                    j = range.next;
                }
            }
            let name_pos = 0u;
            for nm: name in names {
                name_pos += 1u;
                let optid;
                alt find_opt(opts, nm) {
                  some(id) { optid = id; }
                  none. { ret err(unrecognized_option(name_str(nm))); }
                }
                alt opts[optid].hasarg {
                  no. {
                    if !option::is_none::<str>(i_arg) {
                        ret err(unexpected_argument(name_str(nm)));
                    }
                    vals[optid] += [given];
                  }
                  maybe. {
                    if !option::is_none::<str>(i_arg) {
                        vals[optid] += [val(option::get(i_arg))];
                    } else if name_pos < vec::len::<name>(names) ||
                                  i + 1u == l || is_arg(args[i + 1u]) {
                        vals[optid] += [given];
                    } else { i += 1u; vals[optid] += [val(args[i])]; }
                  }
                  yes. {
                    if !option::is_none::<str>(i_arg) {
                        vals[optid] += [val(option::get::<str>(i_arg))];
                    } else if i + 1u == l {
                        ret err(argument_missing(name_str(nm)));
                    } else { i += 1u; vals[optid] += [val(args[i])]; }
                  }
                }
            }
        }
        i += 1u;
    }
    i = 0u;
    while i < n_opts {
        let n = vec::len::<optval>(vals[i]);
        let occ = opts[i].occur;
        if occ == req {
            if n == 0u {
                ret err(option_missing(name_str(opts[i].name)));
            }
        }
        if occ != multi {
            if n > 1u {
                ret err(option_duplicated(name_str(opts[i].name)));
            }
        }
        i += 1u;
    }
    ret ok({opts: opts, vals: vals, free: free});
}

fn opt_vals(m: match, nm: str) -> [optval] {
    ret alt find_opt(m.opts, mkname(nm)) {
          some(id) { m.vals[id] }
          none. { log_err "No option '" + nm + "' defined."; fail }
        };
}

fn opt_val(m: match, nm: str) -> optval { ret opt_vals(m, nm)[0]; }

/*
Function: opt_present

Returns true if an option was matched
*/
fn opt_present(m: match, nm: str) -> bool {
    ret vec::len::<optval>(opt_vals(m, nm)) > 0u;
}

/*
Function: opt_str

Returns the string argument supplied to a matching option

Failure:

- If the option was not matched
- If the match did not take an argument
*/
fn opt_str(m: match, nm: str) -> str {
    ret alt opt_val(m, nm) { val(s) { s } _ { fail } };
}

/*
Function: opt_str

Returns a vector of the arguments provided to all matches of the given option.
Used when an option accepts multiple values.
*/
fn opt_strs(m: match, nm: str) -> [str] {
    let acc: [str] = [];
    for v: optval in opt_vals(m, nm) {
        alt v { val(s) { acc += [s]; } _ { } }
    }
    ret acc;
}

/*
Function: opt_str

Returns the string argument supplied to a matching option or none
*/
fn opt_maybe_str(m: match, nm: str) -> option::t<str> {
    let vals = opt_vals(m, nm);
    if vec::len::<optval>(vals) == 0u { ret none::<str>; }
    ret alt vals[0] { val(s) { some::<str>(s) } _ { none::<str> } };
}

/*
Function: opt_default

Returns the matching string, a default, or none

Returns none if the option was not present, `def` if the option was
present but no argument was provided, and the argument if the option was
present and an argument was provided.
*/
fn opt_default(m: match, nm: str, def: str) -> option::t<str> {
    let vals = opt_vals(m, nm);
    if vec::len::<optval>(vals) == 0u { ret none::<str>; }
    ret alt vals[0] { val(s) { some::<str>(s) } _ { some::<str>(def) } }
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Simplify getopts::find_opt.<commit_after>/*
Module: getopts

Simple getopt alternative. Construct a vector of options, either by using
reqopt, optopt, and optflag or by building them from components yourself, and
pass them to getopts, along with a vector of actual arguments (not including
argv[0]). You'll either get a failure code back, or a match.  You'll have to
verify whether the amount of 'free' arguments in the match is what you
expect. Use opt_* accessors to get argument values out of the match object.

Single-character options are expected to appear on the command line with a
single preceeding dash; multiple-character options are expected to be
proceeded by two dashes. Options that expect an argument accept their argument
following either a space or an equals sign.

Example:

The following example shows simple command line parsing for an application
that requires an input file to be specified, accepts an optional output file
name following -o, and accepts both -h and --help as optional flags.

> fn main(args: [str]) {
>   let opts = [
>     optopt("o"),
>     optflag("h"),
>     optflag("help")
>   ];
>   let match = alt getopts(vec::shift(args), opts) {
>     ok(m) { m }
>     err(f) { fail fail_str(f) }
>   };
>   if opt_present(match, "h") || opt_present(match, "help") {
>     print_usage();
>     ret;
>   }
>   let output = opt_maybe_str(match, "o");
>   let input = if !vec::is_empty(match.free) {
>     match.free[0]
>   } else {
>     print_usage();
>     ret;
>   }
>   do_work(input, output);
> }

*/

import core::result;
import core::result::{err, ok};
import core::option;
import core::option::{some, none};
export opt;
export reqopt;
export optopt;
export optflag;
export optflagopt;
export optmulti;
export getopts;
export match;
export fail_;
export fail_str;
export opt_present;
export opt_str;
export opt_strs;
export opt_maybe_str;
export opt_default;

tag name { long(str); short(char); }

tag hasarg { yes; no; maybe; }

tag occur { req; optional; multi; }

/*
Type: opt

A description of a possible option
*/
type opt = {name: name, hasarg: hasarg, occur: occur};

fn mkname(nm: str) -> name {
    ret if str::char_len(nm) == 1u {
            short(str::char_at(nm, 0u))
        } else { long(nm) };
}

/*
Function: reqopt

Create an option that is required and takes an argument
*/
fn reqopt(name: str) -> opt {
    ret {name: mkname(name), hasarg: yes, occur: req};
}

/*
Function: optopt

Create an option that is optional and takes an argument
*/
fn optopt(name: str) -> opt {
    ret {name: mkname(name), hasarg: yes, occur: optional};
}

/*
Function: optflag

Create an option that is optional and does not take an argument
*/
fn optflag(name: str) -> opt {
    ret {name: mkname(name), hasarg: no, occur: optional};
}

/*
Function: optflagopt

Create an option that is optional and takes an optional argument
*/
fn optflagopt(name: str) -> opt {
    ret {name: mkname(name), hasarg: maybe, occur: optional};
}

/*
Function: optmulti

Create an option that is optional, takes an argument, and may occur
multiple times
*/
fn optmulti(name: str) -> opt {
    ret {name: mkname(name), hasarg: yes, occur: multi};
}

tag optval { val(str); given; }

/*
Type: match

The result of checking command line arguments. Contains a vector
of matches and a vector of free strings.
*/
type match = {opts: [opt], vals: [mutable [optval]], free: [str]};

fn is_arg(arg: str) -> bool {
    ret str::byte_len(arg) > 1u && arg[0] == '-' as u8;
}

fn name_str(nm: name) -> str {
    ret alt nm { short(ch) { str::from_char(ch) } long(s) { s } };
}

fn find_opt(opts: [opt], nm: name) -> option::t<uint> {
    vec::position_pred(opts, { |opt| opt.name == nm })
}

/*
Type: fail_

The type returned when the command line does not conform to the
expected format. Pass this value to <fail_str> to get an error message.
*/
tag fail_ {
    argument_missing(str);
    unrecognized_option(str);
    option_missing(str);
    option_duplicated(str);
    unexpected_argument(str);
}

/*
Function: fail_str

Convert a <fail_> tag into an error string
*/
fn fail_str(f: fail_) -> str {
    ret alt f {
          argument_missing(nm) { "Argument to option '" + nm + "' missing." }
          unrecognized_option(nm) { "Unrecognized option: '" + nm + "'." }
          option_missing(nm) { "Required option '" + nm + "' missing." }
          option_duplicated(nm) {
            "Option '" + nm + "' given more than once."
          }
          unexpected_argument(nm) {
            "Option " + nm + " does not take an argument."
          }
        };
}

/*
Type: result

The result of parsing a command line with a set of options
(result::t<match, fail_>)

Variants:

ok(match) - Returned from getopts on success
err(fail_) - Returned from getopts on failure
*/
type result = result::t<match, fail_>;

/*
Function: getopts

Parse command line arguments according to the provided options

Returns:

ok(match) - On success. Use functions such as <opt_present>
            <opt_str>, etc. to interrogate results.
err(fail_) - On failure. Use <fail_str> to get an error message.
*/
fn getopts(args: [str], opts: [opt]) -> result {
    let n_opts = vec::len::<opt>(opts);
    fn f(_x: uint) -> [optval] { ret []; }
    let vals = vec::init_fn_mut::<[optval]>(f, n_opts);
    let free: [str] = [];
    let l = vec::len(args);
    let i = 0u;
    while i < l {
        let cur = args[i];
        let curlen = str::byte_len(cur);
        if !is_arg(cur) {
            free += [cur];
        } else if str::eq(cur, "--") {
            let j = i + 1u;
            while j < l { free += [args[j]]; j += 1u; }
            break;
        } else {
            let names;
            let i_arg = option::none::<str>;
            if cur[1] == '-' as u8 {
                let tail = str::slice(cur, 2u, curlen);
                let eq = str::index(tail, '=' as u8);
                if eq == -1 {
                    names = [long(tail)];
                } else {
                    names = [long(str::slice(tail, 0u, eq as uint))];
                    i_arg =
                        option::some::<str>(str::slice(tail,
                                                       (eq as uint) + 1u,
                                                       curlen - 2u));
                }
            } else {
                let j = 1u;
                names = [];
                while j < curlen {
                    let range = str::char_range_at(cur, j);
                    names += [short(range.ch)];
                    j = range.next;
                }
            }
            let name_pos = 0u;
            for nm: name in names {
                name_pos += 1u;
                let optid;
                alt find_opt(opts, nm) {
                  some(id) { optid = id; }
                  none. { ret err(unrecognized_option(name_str(nm))); }
                }
                alt opts[optid].hasarg {
                  no. {
                    if !option::is_none::<str>(i_arg) {
                        ret err(unexpected_argument(name_str(nm)));
                    }
                    vals[optid] += [given];
                  }
                  maybe. {
                    if !option::is_none::<str>(i_arg) {
                        vals[optid] += [val(option::get(i_arg))];
                    } else if name_pos < vec::len::<name>(names) ||
                                  i + 1u == l || is_arg(args[i + 1u]) {
                        vals[optid] += [given];
                    } else { i += 1u; vals[optid] += [val(args[i])]; }
                  }
                  yes. {
                    if !option::is_none::<str>(i_arg) {
                        vals[optid] += [val(option::get::<str>(i_arg))];
                    } else if i + 1u == l {
                        ret err(argument_missing(name_str(nm)));
                    } else { i += 1u; vals[optid] += [val(args[i])]; }
                  }
                }
            }
        }
        i += 1u;
    }
    i = 0u;
    while i < n_opts {
        let n = vec::len::<optval>(vals[i]);
        let occ = opts[i].occur;
        if occ == req {
            if n == 0u {
                ret err(option_missing(name_str(opts[i].name)));
            }
        }
        if occ != multi {
            if n > 1u {
                ret err(option_duplicated(name_str(opts[i].name)));
            }
        }
        i += 1u;
    }
    ret ok({opts: opts, vals: vals, free: free});
}

fn opt_vals(m: match, nm: str) -> [optval] {
    ret alt find_opt(m.opts, mkname(nm)) {
          some(id) { m.vals[id] }
          none. { log_err "No option '" + nm + "' defined."; fail }
        };
}

fn opt_val(m: match, nm: str) -> optval { ret opt_vals(m, nm)[0]; }

/*
Function: opt_present

Returns true if an option was matched
*/
fn opt_present(m: match, nm: str) -> bool {
    ret vec::len::<optval>(opt_vals(m, nm)) > 0u;
}

/*
Function: opt_str

Returns the string argument supplied to a matching option

Failure:

- If the option was not matched
- If the match did not take an argument
*/
fn opt_str(m: match, nm: str) -> str {
    ret alt opt_val(m, nm) { val(s) { s } _ { fail } };
}

/*
Function: opt_str

Returns a vector of the arguments provided to all matches of the given option.
Used when an option accepts multiple values.
*/
fn opt_strs(m: match, nm: str) -> [str] {
    let acc: [str] = [];
    for v: optval in opt_vals(m, nm) {
        alt v { val(s) { acc += [s]; } _ { } }
    }
    ret acc;
}

/*
Function: opt_str

Returns the string argument supplied to a matching option or none
*/
fn opt_maybe_str(m: match, nm: str) -> option::t<str> {
    let vals = opt_vals(m, nm);
    if vec::len::<optval>(vals) == 0u { ret none::<str>; }
    ret alt vals[0] { val(s) { some::<str>(s) } _ { none::<str> } };
}

/*
Function: opt_default

Returns the matching string, a default, or none

Returns none if the option was not present, `def` if the option was
present but no argument was provided, and the argument if the option was
present and an argument was provided.
*/
fn opt_default(m: match, nm: str, def: str) -> option::t<str> {
    let vals = opt_vals(m, nm);
    if vec::len::<optval>(vals) == 0u { ret none::<str>; }
    ret alt vals[0] { val(s) { some::<str>(s) } _ { some::<str>(def) } }
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>// Copyright 2013-2015 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.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use io::{IoResult, File};
    use path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::{Ok, Err};
    use slice::SliceExt;
    use mem;
    use os::errno;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(v.slice_from_mut(read));
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as uint;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use io::{IoResult};
    use kinds::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    #[allow(missing_copy_implementations)]
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread::Thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in range(0u, 20) {
            let (tx, rx) = channel();
            txs.push(tx);

            Thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                Thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in range(0u, 100) {
                    r.next_u32();
                    Thread::yield_now();
                    r.next_u64();
                    Thread::yield_now();
                    r.fill_bytes(&mut v);
                    Thread::yield_now();
                }
            }).detach();
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<commit_msg>Use $crate and macro reexport to reduce duplicated code<commit_after>// Copyright 2013-2015 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.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use io::{IoResult, File};
    use path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    // NOTE: for old macros; remove after the next snapshot
    #[cfg(stage0)] use result::Result::Err;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(v.slice_from_mut(read));
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as uint;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use io::{IoResult};
    use kinds::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    #[allow(missing_copy_implementations)]
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread::Thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in range(0u, 20) {
            let (tx, rx) = channel();
            txs.push(tx);

            Thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                Thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in range(0u, 100) {
                    r.next_u32();
                    Thread::yield_now();
                    r.next_u64();
                    Thread::yield_now();
                    r.fill_bytes(&mut v);
                    Thread::yield_now();
                }
            }).detach();
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use crate::io::ErrorKind;
use std::io::{self, BufWriter, Write};
use std::process::exit;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::thread::{self, JoinHandle};

use crate::{constants::*, refiner};
use regex::Regex;
use threadpool::ThreadPool;

const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

lazy_static! {
    static ref STATIC_HEADER_PREFIXES: Vec<(&'static str, &'static str)> = vec![
        ("diff ", BOLD),
        ("index ", BOLD),
        ("--- ", BOLD),
        ("+++ ", BOLD),
        ("@@ ", HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header_prefix in STATIC_HEADER_PREFIXES.iter() {
        let prefix = static_header_prefix.0;
        if line.starts_with(prefix) {
            return static_header_prefix.1;
        }
    }

    return "";
}

fn print<W: io::Write + Send>(stream: &mut BufWriter<W>, text: &str) {
    if let Err(error) = stream.write_all(text.as_bytes()) {
        if error.kind() == ErrorKind::BrokenPipe {
            // This is fine, somebody probably just quit their pager before it
            // was done reading our output.
            exit(0);
        }

        panic!("Error writing diff to pager: {:?}", error);
    }
}

/**
A StringFuture can perform diffing in a background thread.

Doing get() on a future that isn't done yet will block until the result is
available.
*/
struct StringFuture {
    // This field is only valid if we're done with the result_receiver (next
    // field)
    result: String,

    // If available, get() will await a result on this receiver, then populate
    // the result field and return it
    result_receiver: Option<Receiver<String>>,
}

impl StringFuture {
    /// Create an already-finished future
    pub fn from_string(result: String) -> StringFuture {
        return StringFuture {
            result,
            result_receiver: None,
        };
    }

    /// Call get() to get the result of this diff
    pub fn from_oldnew(
        old_text: String,
        new_text: String,
        thread_pool: &ThreadPool,
    ) -> StringFuture {
        // Create a String channel
        let (sender, receiver): (SyncSender<String>, Receiver<String>) = sync_channel(1);

        // Start diffing in a thread
        thread_pool.execute(move || {
            let mut result = String::new();
            for line in refiner::format(&old_text, &new_text) {
                result.push_str(&line);
                result.push('\n');
            }

            // Done, channel the result!
            sender.send(result).unwrap();
        });

        return StringFuture {
            result: "".to_string(),
            result_receiver: Some(receiver),
        };
    }

    pub fn is_empty(&mut self) -> bool {
        return self.get().is_empty();
    }

    pub fn get(&mut self) -> &str {
        // If the result is still pending...
        if let Some(receiver) = &self.result_receiver {
            // ... wait for it
            self.result = receiver.recv().unwrap();
            self.result_receiver = None;
        }

        return &self.result;
    }
}

/**
The way this thing works from the outside is that you initialize it with an
output stream, you pass it one line of input at a time, and it writes
formatted lines to the output stream.

From the inside, it will collect blocks of either diff lines or not-diff-lines.

The not-diff-lines blocks will be enqueued for printing by the printing thread.

The diff lines blocks will also be enqueued for printing, but the actual diffing
will happen in background threads.
*/
pub struct LineCollector {
    old_text: String,
    new_text: String,
    plain_text: String,
    consumer_thread: Option<JoinHandle<()>>,

    diffing_threads: ThreadPool,

    // FIXME: I'd rather have had a SyncSender of some trait here. That would
    // enable us to have two separate result implementations, one which just
    // returns a string and another that does a background computation first.
    // But I failed to figure out how when I tried, more Googling needed!
    queue_putter: SyncSender<StringFuture>,
}

impl Drop for LineCollector {
    fn drop(&mut self) {
        // Flush any outstanding lines. This can be done in any order, at most
        // one of them is going to do anything anyway.
        self.drain_oldnew();
        self.drain_plain();

        // Tell the consumer thread to drain and quit. Sending an empty string
        // like this is the secret handshake for requesting a shutdown.
        self.queue_putter
            .send(StringFuture::from_string("".to_string()))
            .unwrap();

        // Wait for the consumer thread to finish
        // https://stackoverflow.com/q/57670145/473672
        self.consumer_thread.take().map(JoinHandle::join);
    }
}

impl LineCollector {
    pub fn new<W: io::Write + Send + 'static>(output: W) -> LineCollector {
        // The queue will be bounded to 2x the number of logical CPUs.
        //
        // 1x is for the entries that need CPU time for diffing.
        //
        // The other 1x is for the entries that just contain text to print and
        // won't need any background processing time.
        let queue_size = num_cpus::get() * 2;

        // Allocate a queue where we can push our futures to the consumer thread
        let (queue_putter, queue_getter): (SyncSender<StringFuture>, Receiver<StringFuture>) =
            sync_channel(queue_size);

        // This thread takes futures and prints their results
        let consumer = thread::spawn(move || {
            let mut output = BufWriter::new(output);

            loop {
                if let Ok(mut print_me) = queue_getter.recv() {
                    if print_me.is_empty() {
                        // Secret handshake received, done!
                        break;
                    }
                    print(&mut output, print_me.get());
                }
            }
        });

        return LineCollector {
            old_text: String::from(""),
            new_text: String::from(""),
            plain_text: String::from(""),
            consumer_thread: Some(consumer),
            diffing_threads: ThreadPool::new(num_cpus::get()),
            queue_putter,
        };
    }

    fn drain_oldnew(&mut self) {
        if self.old_text.is_empty() && self.new_text.is_empty() {
            return;
        }

        self.queue_putter
            .send(StringFuture::from_oldnew(
                self.old_text.clone(),
                self.new_text.clone(),
                &self.diffing_threads,
            ))
            .unwrap();

        self.old_text.clear();
        self.new_text.clear();
    }

    fn drain_plain(&mut self) {
        if self.plain_text.is_empty() {
            return;
        }

        // Enqueue an already-resolved future
        self.queue_putter
            .send(StringFuture::from_string(String::from(&self.plain_text)))
            .unwrap();

        self.plain_text.clear();
    }

    fn consume_plain_line(&mut self, line: &str) {
        self.drain_oldnew();
        self.plain_text.push_str(line);
        self.plain_text.push('\n');
    }

    fn consume_old_line(&mut self, line: &str) {
        self.drain_plain();
        self.old_text.push_str(&line[1..]);
        self.old_text.push('\n');
    }

    fn consume_new_line(&mut self, line: &str) {
        self.drain_plain();
        self.new_text.push_str(&line[1..]);
        self.new_text.push('\n');
    }

    fn consume_no_eof_newline_marker(&mut self) {
        if !self.new_text.is_empty() {
            // New section comes after old, so if we get in here it's a new
            // section that doesn't end in a newline. Remove its trailing
            // newline.
            assert!(self.new_text.pop().unwrap() == '\n');
            return;
        }

        if !self.old_text.is_empty() {
            // Old text doesn't end in a newline, remove its trailing newline
            assert!(self.old_text.pop().unwrap() == '\n');
            return;
        }

        // It's a piece of unchanged text that doesn't end in a newline, just
        // consume the colorized marker as plain text
        self.consume_plain_line(&format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, &NO_EOF_NEWLINE_MARKER, NORMAL
        ))
    }

    pub fn consume_line(&mut self, line: String) {
        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            self.consume_plain_line(&format!("{}{}{}", fixed_highlight, line, NORMAL));
            return;
        }

        if line.is_empty() {
            self.consume_plain_line("");
            return;
        }

        if line.starts_with('-') {
            self.consume_old_line(&line);
            return;
        }

        if line.starts_with('+') {
            self.consume_new_line(&line);
            return;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            self.consume_no_eof_newline_marker();
            return;
        }

        self.consume_plain_line(&line);
    }
}
<commit_msg>Remove a format!() call<commit_after>use crate::io::ErrorKind;
use std::io::{self, BufWriter, Write};
use std::process::exit;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::thread::{self, JoinHandle};

use crate::{constants::*, refiner};
use regex::Regex;
use threadpool::ThreadPool;

const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

lazy_static! {
    static ref STATIC_HEADER_PREFIXES: Vec<(&'static str, &'static str)> = vec![
        ("diff ", BOLD),
        ("index ", BOLD),
        ("--- ", BOLD),
        ("+++ ", BOLD),
        ("@@ ", HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header_prefix in STATIC_HEADER_PREFIXES.iter() {
        let prefix = static_header_prefix.0;
        if line.starts_with(prefix) {
            return static_header_prefix.1;
        }
    }

    return "";
}

fn print<W: io::Write + Send>(stream: &mut BufWriter<W>, text: &str) {
    if let Err(error) = stream.write_all(text.as_bytes()) {
        if error.kind() == ErrorKind::BrokenPipe {
            // This is fine, somebody probably just quit their pager before it
            // was done reading our output.
            exit(0);
        }

        panic!("Error writing diff to pager: {:?}", error);
    }
}

/**
A StringFuture can perform diffing in a background thread.

Doing get() on a future that isn't done yet will block until the result is
available.
*/
struct StringFuture {
    // This field is only valid if we're done with the result_receiver (next
    // field)
    result: String,

    // If available, get() will await a result on this receiver, then populate
    // the result field and return it
    result_receiver: Option<Receiver<String>>,
}

impl StringFuture {
    /// Create an already-finished future
    pub fn from_string(result: String) -> StringFuture {
        return StringFuture {
            result,
            result_receiver: None,
        };
    }

    /// Call get() to get the result of this diff
    pub fn from_oldnew(
        old_text: String,
        new_text: String,
        thread_pool: &ThreadPool,
    ) -> StringFuture {
        // Create a String channel
        let (sender, receiver): (SyncSender<String>, Receiver<String>) = sync_channel(1);

        // Start diffing in a thread
        thread_pool.execute(move || {
            let mut result = String::new();
            for line in refiner::format(&old_text, &new_text) {
                result.push_str(&line);
                result.push('\n');
            }

            // Done, channel the result!
            sender.send(result).unwrap();
        });

        return StringFuture {
            result: "".to_string(),
            result_receiver: Some(receiver),
        };
    }

    pub fn is_empty(&mut self) -> bool {
        return self.get().is_empty();
    }

    pub fn get(&mut self) -> &str {
        // If the result is still pending...
        if let Some(receiver) = &self.result_receiver {
            // ... wait for it
            self.result = receiver.recv().unwrap();
            self.result_receiver = None;
        }

        return &self.result;
    }
}

/**
The way this thing works from the outside is that you initialize it with an
output stream, you pass it one line of input at a time, and it writes
formatted lines to the output stream.

From the inside, it will collect blocks of either diff lines or not-diff-lines.

The not-diff-lines blocks will be enqueued for printing by the printing thread.

The diff lines blocks will also be enqueued for printing, but the actual diffing
will happen in background threads.
*/
pub struct LineCollector {
    old_text: String,
    new_text: String,
    plain_text: String,
    consumer_thread: Option<JoinHandle<()>>,

    diffing_threads: ThreadPool,

    // FIXME: I'd rather have had a SyncSender of some trait here. That would
    // enable us to have two separate result implementations, one which just
    // returns a string and another that does a background computation first.
    // But I failed to figure out how when I tried, more Googling needed!
    queue_putter: SyncSender<StringFuture>,
}

impl Drop for LineCollector {
    fn drop(&mut self) {
        // Flush any outstanding lines. This can be done in any order, at most
        // one of them is going to do anything anyway.
        self.drain_oldnew();
        self.drain_plain();

        // Tell the consumer thread to drain and quit. Sending an empty string
        // like this is the secret handshake for requesting a shutdown.
        self.queue_putter
            .send(StringFuture::from_string("".to_string()))
            .unwrap();

        // Wait for the consumer thread to finish
        // https://stackoverflow.com/q/57670145/473672
        self.consumer_thread.take().map(JoinHandle::join);
    }
}

impl LineCollector {
    pub fn new<W: io::Write + Send + 'static>(output: W) -> LineCollector {
        // The queue will be bounded to 2x the number of logical CPUs.
        //
        // 1x is for the entries that need CPU time for diffing.
        //
        // The other 1x is for the entries that just contain text to print and
        // won't need any background processing time.
        let queue_size = num_cpus::get() * 2;

        // Allocate a queue where we can push our futures to the consumer thread
        let (queue_putter, queue_getter): (SyncSender<StringFuture>, Receiver<StringFuture>) =
            sync_channel(queue_size);

        // This thread takes futures and prints their results
        let consumer = thread::spawn(move || {
            let mut output = BufWriter::new(output);

            loop {
                if let Ok(mut print_me) = queue_getter.recv() {
                    if print_me.is_empty() {
                        // Secret handshake received, done!
                        break;
                    }
                    print(&mut output, print_me.get());
                }
            }
        });

        return LineCollector {
            old_text: String::from(""),
            new_text: String::from(""),
            plain_text: String::from(""),
            consumer_thread: Some(consumer),
            diffing_threads: ThreadPool::new(num_cpus::get()),
            queue_putter,
        };
    }

    fn drain_oldnew(&mut self) {
        if self.old_text.is_empty() && self.new_text.is_empty() {
            return;
        }

        self.queue_putter
            .send(StringFuture::from_oldnew(
                self.old_text.clone(),
                self.new_text.clone(),
                &self.diffing_threads,
            ))
            .unwrap();

        self.old_text.clear();
        self.new_text.clear();
    }

    fn drain_plain(&mut self) {
        if self.plain_text.is_empty() {
            return;
        }

        // Enqueue an already-resolved future
        self.queue_putter
            .send(StringFuture::from_string(String::from(&self.plain_text)))
            .unwrap();

        self.plain_text.clear();
    }

    fn consume_plain_line(&mut self, line: &str) {
        self.drain_oldnew();
        self.plain_text.push_str(line);
        self.plain_text.push('\n');
    }

    /// Like consume_plain_line(), but without outputting any trailing linefeed.
    fn consume_plain_linepart(&mut self, linepart: &str) {
        self.drain_oldnew();
        self.plain_text.push_str(linepart);
    }

    fn consume_old_line(&mut self, line: &str) {
        self.drain_plain();
        self.old_text.push_str(&line[1..]);
        self.old_text.push('\n');
    }

    fn consume_new_line(&mut self, line: &str) {
        self.drain_plain();
        self.new_text.push_str(&line[1..]);
        self.new_text.push('\n');
    }

    fn consume_no_eof_newline_marker(&mut self) {
        if !self.new_text.is_empty() {
            // New section comes after old, so if we get in here it's a new
            // section that doesn't end in a newline. Remove its trailing
            // newline.
            assert!(self.new_text.pop().unwrap() == '\n');
            return;
        }

        if !self.old_text.is_empty() {
            // Old text doesn't end in a newline, remove its trailing newline
            assert!(self.old_text.pop().unwrap() == '\n');
            return;
        }

        // It's a piece of unchanged text that doesn't end in a newline, just
        // consume the colorized marker as plain text
        self.consume_plain_line(&format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, &NO_EOF_NEWLINE_MARKER, NORMAL
        ))
    }

    pub fn consume_line(&mut self, line: String) {
        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            self.consume_plain_linepart(fixed_highlight);
            self.consume_plain_linepart(&line);
            self.consume_plain_line(NORMAL); // consume_plain_line() will add a linefeed to the output
            return;
        }

        if line.is_empty() {
            self.consume_plain_line("");
            return;
        }

        if line.starts_with('-') {
            self.consume_old_line(&line);
            return;
        }

        if line.starts_with('+') {
            self.consume_new_line(&line);
            return;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            self.consume_no_eof_newline_marker();
            return;
        }

        self.consume_plain_line(&line);
    }
}
<|endoftext|>


<commit_before>#![feature(concat_idents, custom_derive, plugin)]
#![plugin(serde_macros)]
extern crate serde;
extern crate tarpc;
#[macro_use]
extern crate log;

#[macro_export]
macro_rules! rpc_service $server:ident {
    { $( $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty;)* } => {
        mod $server {
            use std::net::{
                TcpStream,
                ToSocketAddrs,
            };
            use std::io;
            use std::sync::Arc;
            use tarpc::{
                self,
                Shutdown,
                serve_async,
            };

            #[derive(Debug)]
            pub enum Error {
                Io(io::Error),
                InternalError,
            }

            impl ::std::convert::From<tarpc::Error> for Error {
                fn from(err: tarpc::Error) -> Error {
                    match err {
                        tarpc::Error::Io(err) => Error::Io(err),
                        _ => Error::InternalError,
                    }
                }
            }

            impl ::std::convert::From<io::Error> for Error {
                fn from(err: io::Error) -> Error {
                    Error::Io(err)
                }
            }

            pub type Result<T> = ::std::result::Result<T, Error>;

            pub trait Service: Send + Sync {
                $(
                    fn $fn_name(&self, $($arg:$in_),*) -> $out;
                )*
            }

            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum Request {
                $(
                    $fn_name($($in_),*),
                )*
            }

            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum Reply {
                $(
                    $fn_name($out),
                )*
            }

            pub struct Client(tarpc::Client<Request, Reply>);

            impl Client {
                pub fn new<A>(addr: A) -> Result<Self>
                    where A: ToSocketAddrs,
                {
                    let stream = try!(TcpStream::connect(addr));
                    let inner = try!(tarpc::Client::new(stream));
                    Ok(Client(inner))
                }

                $(
                    pub fn $fn_name(&self, $($arg: $in_),*) -> Result<$out> {
                        let reply = try!((self.0).rpc(&Request::$fn_name($($arg),*)));
                        match reply {
                            Reply::$fn_name(reply) => Ok(reply),
                            _ => Err(Error::InternalError),
                        }
                    }
                )*
            }

            pub struct Server<S: 'static + Service>(S);

            impl<S> tarpc::Serve<Request, Reply> for Server<S>
                where S: 'static + Service
            {
                fn serve(&self, request: Request) -> Reply {
                    match request {
                        $(
                            Request::$fn_name($($arg),*) =>
                                Reply::$fn_name((self.0).$fn_name($($arg),*)),
                         )*
                    }
                }
            }

            pub fn serve<A, S>(addr: A, service: S) -> Result<Shutdown>
                where A: ToSocketAddrs,
                      S: 'static + Service
            {
                let server = Arc::new(Server(service));
                Ok(try!(serve_async(addr, server)))
            }
        }
    }
}

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

    rpc_service! my_server {
        hello(foo: super::Foo) -> super::Foo;
        add(x: i32, y: i32) -> i32;
    };

    #[derive(PartialEq, Debug, Serialize, Deserialize)]
    pub struct Foo {
        message: String
    }

    impl Service for () {
        fn hello(&self, s: Foo) -> Foo {
            Foo{message: format!("Hello, {}", &s.message)}
        }

        fn add(&self, x: i32, y: i32) -> i32 {
            x + y
        }
    }

    #[test]
    fn simple_test() {
        println!("Starting");
        let addr = "127.0.0.1:9000";
        let shutdown = my_server::serve(addr, ()).unwrap();
        let client = Client::new(addr).unwrap();
        assert_eq!(3, client.add(1, 2).unwrap());
        let foo = Foo{message: "Adam".into()};
        let want = Foo{message: format!("Hello, {}", &foo.message)};
        assert_eq!(want, client.hello(Foo{message: "Adam".into()}).unwrap());
        drop(client);
        shutdown.shutdown();
    }
}
<commit_msg>Fix syntax and add doc comments to public items created in the macro<commit_after>//! Provides a macro for creating an rpc service and client stub.
//! Ex:
//! ```
//! #[macro_use]
//! extern crate tarpc_macros;
//!
//! rpc_service!(my_server:
//!     hello(name: String) -> String;
//!     add(x: i32, y: i32) -> i32;
//! );
//!
//! use self::my_server::*;
//!
//! impl my_server::Service for () {
//!     fn hello(&self, s: String) -> Foo {
//!         format!("Hello, {}!", s)
//!     }
//!     fn add(&self, x: i32, y: i32) -> i32 {
//!         x + y
//!     }
//! }
//!
//! fn main() {
//!     let addr = "127.0.0.1:9000";
//!     let shutdown = my_server::serve(addr, ()).unwrap();
//!     let client = Client::new(addr).unwrap();
//!     assert_eq!(3, client.add(1, 2).unwrap());
//!     assert_eq!("Hello, Mom!".into(), client.hello("Mom").unwrap());
//!     drop(client);
//!     shutdown.shutdown();
//! }
//! ```
#![feature(concat_idents, custom_derive, plugin)]
#![plugin(serde_macros)]
extern crate serde;
extern crate tarpc;
#[macro_use]
extern crate log;

#[macro_export]
macro_rules! rpc_service { ($server:ident:
    $( $fn_name:ident( $( $arg:ident : $in_:ty ),* ) -> $out:ty;)*) => {
        mod $server {
            use std::net::{
                TcpStream,
                ToSocketAddrs,
            };
            use std::io;
            use std::sync::Arc;
            use tarpc::{
                self,
                Shutdown,
                serve_async,
            };

            #[doc="An RPC error that occurred during serving an RPC request."]
            #[derive(Debug)]
            pub enum Error {
                #[doc="An IO error occurred."]
                Io(io::Error),

                #[doc="An unexpected internal error. Typically a bug in the server impl."]
                InternalError,
            }

            impl ::std::convert::From<tarpc::Error> for Error {
                fn from(err: tarpc::Error) -> Error {
                    match err {
                        tarpc::Error::Io(err) => Error::Io(err),
                        _ => Error::InternalError,
                    }
                }
            }

            impl ::std::convert::From<io::Error> for Error {
                fn from(err: io::Error) -> Error {
                    Error::Io(err)
                }
            }

            #[doc="The result of an RPC call; either the successful result or the error."]
            pub type Result<T> = ::std::result::Result<T, Error>;

            #[doc="The provided RPC service."]
            pub trait Service: Send + Sync {
                $(
                    fn $fn_name(&self, $($arg:$in_),*) -> $out;
                )*
            }

            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum Request {
                $(
                    $fn_name($($in_),*),
                )*
            }

            #[allow(non_camel_case_types)]
            #[derive(Debug, Serialize, Deserialize)]
            enum Reply {
                $(
                    $fn_name($out),
                )*
            }

            #[doc="The client stub that makes RPC calls to the server."]
            pub struct Client(tarpc::Client<Request, Reply>);

            impl Client {
                #[doc="Create a new client that connects to the given address."]
                pub fn new<A>(addr: A) -> Result<Self>
                    where A: ToSocketAddrs,
                {
                    let stream = try!(TcpStream::connect(addr));
                    let inner = try!(tarpc::Client::new(stream));
                    Ok(Client(inner))
                }

                $(
                    pub fn $fn_name(&self, $($arg: $in_),*) -> Result<$out> {
                        let reply = try!((self.0).rpc(&Request::$fn_name($($arg),*)));
                        match reply {
                            Reply::$fn_name(reply) => Ok(reply),
                            _ => Err(Error::InternalError),
                        }
                    }
                )*
            }

            struct Server<S: 'static + Service>(S);

            impl<S> tarpc::Serve<Request, Reply> for Server<S>
                where S: 'static + Service
            {
                fn serve(&self, request: Request) -> Reply {
                    match request {
                        $(
                            Request::$fn_name($($arg),*) =>
                                Reply::$fn_name((self.0).$fn_name($($arg),*)),
                         )*
                    }
                }
            }

            #[doc="Start a running service."]
            pub fn serve<A, S>(addr: A, service: S) -> Result<Shutdown>
                where A: ToSocketAddrs,
                      S: 'static + Service
            {
                let server = Arc::new(Server(service));
                Ok(try!(serve_async(addr, server)))
            }
        }
    }
}

#[cfg(test)]
mod test {

    rpc_service!(my_server:
        hello(foo: super::Foo) -> super::Foo;

        add(x: i32, y: i32) -> i32;
    );

    use self::my_server::*;

    #[derive(PartialEq, Debug, Serialize, Deserialize)]
    pub struct Foo {
        message: String
    }

    impl Service for () {
        fn hello(&self, s: Foo) -> Foo {
            Foo{message: format!("Hello, {}", &s.message)}
        }

        fn add(&self, x: i32, y: i32) -> i32 {
            x + y
        }
    }

    #[test]
    fn simple_test() {
        println!("Starting");
        let addr = "127.0.0.1:9000";
        let shutdown = my_server::serve(addr, ()).unwrap();
        let client = Client::new(addr).unwrap();
        assert_eq!(3, client.add(1, 2).unwrap());
        let foo = Foo{message: "Adam".into()};
        let want = Foo{message: format!("Hello, {}", &foo.message)};
        assert_eq!(want, client.hello(Foo{message: "Adam".into()}).unwrap());
        drop(client);
        shutdown.shutdown();
    }
}
<|endoftext|>


<commit_before>extern crate moho;
extern crate sdl2_image;
extern crate sdl2;
extern crate glm;

use self::sdl2::event::Event;
use self::sdl2::keyboard::Keycode;
use self::sdl2::render::{Renderer, Texture};
use self::sdl2::rect;

use self::sdl2_image::{LoadTexture, INIT_PNG, INIT_JPG};
use std::error::Error;
use std::path::Path;

pub fn run() -> Result<(), Box<Error>> {
    const WINDOW_HEIGHT: u32 = 600;
    const WINDOW_WIDTH: u32 = 800;

    let (mut renderer, mut event_pump) =
        try!(moho::init("Master Smasher", WINDOW_WIDTH, WINDOW_HEIGHT));
    let _image_context = try!(sdl2_image::init(INIT_PNG | INIT_JPG));
    let mut input_manager = moho::input_manager::InputManager::new();
    let background_path = Path::new("resources/background_game.png");
    let meteor_path = Path::new("resources/meteor.png");

    let background = Background { texture: try!(renderer.load_texture(background_path)) };
    let mut meteor = Meteor::new(try!(renderer.load_texture(meteor_path)),
                                 glm::uvec2(WINDOW_WIDTH, WINDOW_HEIGHT));

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Some(key), .. } => {
                    input_manager.press_key(key);
                }
                Event::KeyUp { keycode: Some(key), .. } => {
                    input_manager.release_key(key);
                }
                _ => {}
            }
        }
        if input_manager.is_key_down(Keycode::Escape) {
            break;
        }
        meteor.update(&input_manager);

        renderer.clear();
        try!(background.draw(&mut renderer));
        try!(meteor.draw(&mut renderer));
        renderer.present();
    }

    Ok(())
}

struct Background {
    texture: Texture,
}

impl Background {
    fn draw(&self, renderer: &mut Renderer) -> Result<(), Box<Error>> {
        try!(renderer.copy(&self.texture, None, None));

        Ok(())
    }
}

struct Meteor {
    texture: Texture,
    rect: rect::Rect,
    max_coords: glm::Vector2<u32>,
}

impl Meteor {
    fn new(texture: Texture, max_coords: glm::Vector2<u32>) -> Self {
        let query = texture.query();
        let rect = rect::Rect::new(0, 0, query.width, query.height);

        Meteor {
            texture: texture,
            rect: rect,
            max_coords: max_coords,
        }
    }

    fn update(&mut self, input_manager: &moho::input_manager::InputManager) {
        let mut top = self.rect.y();
        let mut left = self.rect.x();

        if input_manager.is_key_down(Keycode::Down) {
            top += 1;
        }

        if input_manager.is_key_down(Keycode::Up) {
            top -= 1;
        }

        if input_manager.is_key_down(Keycode::Left) {
            left -= 1;
        }

        if input_manager.is_key_down(Keycode::Right) {
            left += 1;
        }

        let max_height = self.max_coords.y as i32;
        let max_width = self.max_coords.x as i32;

        self.rect.set_y((top + max_height) % max_height);
        self.rect.set_x((left + max_width) % max_width);
    }

    fn draw(&self, renderer: &mut Renderer) -> Result<(), Box<Error>> {
        try!(renderer.copy(&self.texture, None, Some(self.rect)));

        let max_height = self.max_coords.y as i32;
        let max_width = self.max_coords.x as i32;
        let bottom = self.rect.bottom();
        let right = self.rect.right();

        if bottom > max_height {
            let mut wrapping_rect = self.rect.clone();
            wrapping_rect.set_bottom(bottom % max_height);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
        }

        if right > max_width {
            let mut wrapping_rect = self.rect.clone();
            wrapping_rect.set_right(right % max_width);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
        }

        if bottom > max_height && right > max_width {
            let mut wrapping_rect = self.rect.clone();
            wrapping_rect.set_bottom(bottom % max_height);
            wrapping_rect.set_right(right % max_width);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
        }

        Ok(())
    }
}
<commit_msg>launch meteor with a mouse click<commit_after>extern crate moho;
extern crate sdl2_image;
extern crate sdl2;
extern crate glm;

use self::sdl2::event::Event;
use self::sdl2::keyboard::Keycode;
use self::sdl2::render::{Renderer, Texture};
use self::sdl2::rect;

use self::sdl2_image::{LoadTexture, INIT_PNG, INIT_JPG};
use std::error::Error;
use std::path::Path;

pub fn run() -> Result<(), Box<Error>> {
    const WINDOW_HEIGHT: u32 = 600;
    const WINDOW_WIDTH: u32 = 800;

    let (mut renderer, mut event_pump) =
        try!(moho::init("Master Smasher", WINDOW_WIDTH, WINDOW_HEIGHT));
    let _image_context = try!(sdl2_image::init(INIT_PNG | INIT_JPG));
    let mut input_manager = moho::input_manager::InputManager::new();
    let background_path = Path::new("resources/background_game.png");
    let meteor_path = Path::new("resources/meteor.png");

    let background = Background { texture: try!(renderer.load_texture(background_path)) };
    let mut meteor = Meteor::new(try!(renderer.load_texture(meteor_path)),
                                 glm::uvec2(WINDOW_WIDTH, WINDOW_HEIGHT));

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Some(key), .. } => {
                    input_manager.press_key(key);
                }
                Event::KeyUp { keycode: Some(key), .. } => {
                    input_manager.release_key(key);
                }
                Event::MouseButtonDown { x, y, .. } => {
                    if !meteor.launched || true {
                        meteor.launch(glm::dvec2(x as f64, y as f64));
                    }
                }
                _ => {}
            }
        }
        if input_manager.is_key_down(Keycode::Escape) {
            break;
        }
        meteor.update();

        renderer.clear();
        try!(background.draw(&mut renderer));
        try!(meteor.draw(&mut renderer));
        renderer.present();
    }

    Ok(())
}

struct Background {
    texture: Texture,
}

impl Background {
    fn draw(&self, renderer: &mut Renderer) -> Result<(), Box<Error>> {
        try!(renderer.copy(&self.texture, None, None));

        Ok(())
    }
}

struct Meteor {
    texture: Texture,
    center: glm::Vector2<f64>,
    dims: glm::Vector2<u32>,
    max_coords: glm::Vector2<u32>,
    velocity: glm::Vector2<f64>,
    launched: bool,
}

impl Meteor {
    fn new(texture: Texture, max_coords: glm::Vector2<u32>) -> Self {
        let query = texture.query();
        let center = glm::dvec2(0., 0.);
        let dims = glm::uvec2(query.width, query.height);

        Meteor {
            texture: texture,
            center: center,
            dims: dims,
            max_coords: max_coords,
            velocity: glm::dvec2(0., 0.),
            launched: false,
        }
    }

    fn launch(&mut self, target: glm::Vector2<f64>) {
        const FACTOR: f64 = 85.;
        let offset = target - self.center;
        self.velocity = glm::dvec2(offset.x as f64 / FACTOR, offset.y as f64 / FACTOR);
        self.launched = true;
    }

    fn update(&mut self) {
        self.center.y += self.velocity.y;
        self.center.x += self.velocity.x;

        let max_height = self.max_coords.y as f64;
        let max_width = self.max_coords.x as f64;

        self.center.y = (self.center.y + max_height) % max_height;
        self.center.x = (self.center.x + max_width) % max_width;
    }

    fn draw(&self, renderer: &mut Renderer) -> Result<(), Box<Error>> {
        let top = self.center.y as i32 - self.dims.y as i32 / 2;
        let bottom = self.center.y as i32 + self.dims.y as i32 / 2;
        let left = self.center.x as i32 - self.dims.x as i32 / 2;
        let right = self.center.x as i32 + self.dims.x as i32 / 2;

        let rect = rect::Rect::new(left, top, self.dims.x, self.dims.y);
        try!(renderer.copy(&self.texture, None, Some(rect)));

        let max_height = self.max_coords.y as i32;
        let max_width = self.max_coords.x as i32;

        if top < 0 {
            let mut wrapping_rect = rect.clone();
            wrapping_rect.set_y(top + max_height);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));

            if left < 0 {
                wrapping_rect = wrapping_rect.clone();
                wrapping_rect.set_x(left + max_width);
                try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
            } else if right > max_width {
                wrapping_rect = wrapping_rect.clone();
                wrapping_rect.set_right(right % max_width);
                try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
            }
        } else if bottom > max_height {
            let mut wrapping_rect = rect.clone();
            wrapping_rect.set_bottom(bottom % max_height);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));

            if left < 0 {
                wrapping_rect = wrapping_rect.clone();
                wrapping_rect.set_x(left + max_width);
                try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
            } else if right > max_width {
                wrapping_rect = wrapping_rect.clone();
                wrapping_rect.set_right(right % max_width);
                try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
            }
        }

        if left < 0 {
            let mut wrapping_rect = rect.clone();
            wrapping_rect.set_x(left + max_width);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
        } else if right > max_width {
            let mut wrapping_rect = rect.clone();
            wrapping_rect.set_right(right % max_width);
            try!(renderer.copy(&self.texture, None, Some(wrapping_rect)));
        }

        Ok(())
    }
}
<|endoftext|>