<commit_before>//! Module converting command-line arguments into test configuration.

use getopts;
use std::env;
use std::path::PathBuf;

use super::helpers::isatty;
use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
use super::time::TestTimeOptions;

#[derive(Debug)]
pub struct TestOpts {
    pub list: bool,
    pub filter: Option<String>,
    pub filter_exact: bool,
    pub force_run_in_process: bool,
    pub exclude_should_panic: bool,
    pub run_ignored: RunIgnored,
    pub run_tests: bool,
    pub bench_benchmarks: bool,
    pub logfile: Option<PathBuf>,
    pub nocapture: bool,
    pub color: ColorConfig,
    pub format: OutputFormat,
    pub test_threads: Option<usize>,
    pub skip: Vec<String>,
    pub time_options: Option<TestTimeOptions>,
    pub options: Options,
}

impl TestOpts {
    pub fn use_color(&self) -> bool {
        match self.color {
            ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
            ColorConfig::AlwaysColor => true,
            ColorConfig::NeverColor => false,
        }
    }
}

/// Result of parsing the options.
pub type OptRes = Result<TestOpts, String>;
/// Result of parsing the option part.
type OptPartRes<T> = Result<T, String>;

fn optgroups() -> getopts::Options {
    let mut opts = getopts::Options::new();
    opts.optflag("", "include-ignored", "Run ignored and not ignored tests")
        .optflag("", "ignored", "Run only ignored tests")
        .optflag("", "force-run-in-process", "Forces tests to run in-process when panic=abort")
        .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic")
        .optflag("", "test", "Run tests and not benchmarks")
        .optflag("", "bench", "Run benchmarks instead of tests")
        .optflag("", "list", "List all tests and benchmarks")
        .optflag("h", "help", "Display this message (longer with --help)")
        .optopt(
            "",
            "logfile",
            "Write logs to the specified file instead \
             of stdout",
            "PATH",
        )
        .optflag(
            "",
            "nocapture",
            "don't capture stdout/stderr of each \
             task, allow printing directly",
        )
        .optopt(
            "",
            "test-threads",
            "Number of threads used for running tests \
             in parallel",
            "n_threads",
        )
        .optmulti(
            "",
            "skip",
            "Skip tests whose names contain FILTER (this flag can \
             be used multiple times)",
            "FILTER",
        )
        .optflag(
            "q",
            "quiet",
            "Display one character per test instead of one line. \
             Alias to --format=terse",
        )
        .optflag("", "exact", "Exactly match filters rather than by substring")
        .optopt(
            "",
            "color",
            "Configure coloring of output:
            auto   = colorize if stdout is a tty and tests are run on serially (default);
            always = always colorize output;
            never  = never colorize output;",
            "auto|always|never",
        )
        .optopt(
            "",
            "format",
            "Configure formatting of output:
            pretty = Print verbose output;
            terse  = Display one character per test;
            json   = Output a json document",
            "pretty|terse|json",
        )
        .optflag("", "show-output", "Show captured stdout of successful tests")
        .optopt(
            "Z",
            "",
            "Enable nightly-only flags:
            unstable-options = Allow use of experimental features",
            "unstable-options",
        )
        .optflagopt(
            "",
            "report-time",
            "Show execution time of each test. Awailable values:
            plain   = do not colorize the execution time (default);
            colored = colorize output according to the `color` parameter value;

            Threshold values for colorized output can be configured via
            `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
            `RUST_TEST_TIME_DOCTEST` environment variables.

            Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.

            Not available for --format=terse",
            "plain|colored",
        )
        .optflag(
            "",
            "ensure-time",
            "Treat excess of the test execution time limit as error.

            Threshold values for this option can be configured via
            `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
            `RUST_TEST_TIME_DOCTEST` environment variables.

            Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.

            `CRITICAL_TIME` here means the limit that should not be exceeded by test.
            ",
        );
    opts
}

fn usage(binary: &str, options: &getopts::Options) {
    let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
    println!(
        r#"{usage}

The FILTER string is tested against the name of all tests, and only those
tests whose names contain the filter are run.

By default, all tests are run in parallel. This can be altered with the
--test-threads flag or the RUST_TEST_THREADS environment variable when running
tests (set it to 1).

All tests have their standard output and standard error captured by default.
This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
environment variable to a value other than "0". Logging is not captured by default.

Test Attributes:

    `#[test]`        - Indicates a function is a test to be run. This function
                       takes no arguments.
    `#[bench]`       - Indicates a function is a benchmark to be run. This
                       function takes one argument (test::Bencher).
    `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
                        the code causes a panic (an assertion failure or panic!)
                        A message may be provided, which the failure string must
                        contain: #[should_panic(expected = "foo")].
    `#[ignore]`       - When applied to a function which is already attributed as a
                        test, then the test runner will ignore these tests during
                        normal test runs. Running with --ignored or --include-ignored will run
                        these tests."#,
        usage = options.usage(&message)
    );
}

/// Parses command line arguments into test options.
/// Returns `None` if help was requested (since we only show help message and don't run tests),
/// returns `Some(Err(..))` if provided arguments are incorrect,
/// otherwise creates a `TestOpts` object and returns it.
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
    // Parse matches.
    let opts = optgroups();
    let args = args.get(1..).unwrap_or(args);
    let matches = match opts.parse(args) {
        Ok(m) => m,
        Err(f) => return Some(Err(f.to_string())),
    };

    // Check if help was requested.
    if matches.opt_present("h") {
        // Show help and do nothing more.
        usage(&args[0], &opts);
        return None;
    }

    // Actually parse the opts.
    let opts_result = parse_opts_impl(matches);

    Some(opts_result)
}

// Gets the option value and checks if unstable features are enabled.
macro_rules! unstable_optflag {
    ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
        let opt = $matches.opt_present($option_name);
        if !$allow_unstable && opt {
            return Err(format!(
                "The \"{}\" flag is only accepted on the nightly compiler",
                $option_name
            ));
        }

        opt
    }};
}

// Implementation of `parse_opts` that doesn't care about help message
// and returns a `Result`.
fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
    let allow_unstable = get_allow_unstable(&matches)?;

    // Unstable flags
    let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
    let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
    let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
    let time_options = get_time_options(&matches, allow_unstable)?;

    let quiet = matches.opt_present("quiet");
    let exact = matches.opt_present("exact");
    let list = matches.opt_present("list");
    let skip = matches.opt_strs("skip");

    let bench_benchmarks = matches.opt_present("bench");
    let run_tests = !bench_benchmarks || matches.opt_present("test");

    let logfile = get_log_file(&matches)?;
    let run_ignored = get_run_ignored(&matches, include_ignored)?;
    let filter = get_filter(&matches)?;
    let nocapture = get_nocapture(&matches)?;
    let test_threads = get_test_threads(&matches)?;
    let color = get_color_config(&matches)?;
    let format = get_format(&matches, quiet, allow_unstable)?;

    let options = Options::new().display_output(matches.opt_present("show-output"));

    let test_opts = TestOpts {
        list,
        filter,
        filter_exact: exact,
        force_run_in_process,
        exclude_should_panic,
        run_ignored,
        run_tests,
        bench_benchmarks,
        logfile,
        nocapture,
        color,
        format,
        test_threads,
        skip,
        time_options,
        options,
    };

    Ok(test_opts)
}

// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
fn is_nightly() -> bool {
    // Whether this is a feature-staged build, i.e., on the beta or stable channel
    let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
    // Whether we should enable unstable features for bootstrapping
    let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();

    bootstrap || !disable_unstable_features
}

// Gets the CLI options assotiated with `report-time` feature.
fn get_time_options(
    matches: &getopts::Matches,
    allow_unstable: bool,
) -> OptPartRes<Option<TestTimeOptions>> {
    let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
    let colored_opt_str = matches.opt_str("report-time");
    let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
    let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");

    // If `ensure-test-time` option is provided, time output is enforced,
    // so user won't be confused if any of tests will silently fail.
    let options = if report_time || ensure_test_time {
        if ensure_test_time && !report_time {
            report_time_colored = true;
        }
        Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
    } else {
        None
    };

    Ok(options)
}

fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
    let test_threads = match matches.opt_str("test-threads") {
        Some(n_str) => match n_str.parse::<usize>() {
            Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
            Ok(n) => Some(n),
            Err(e) => {
                return Err(format!(
                    "argument for --test-threads must be a number > 0 \
                     (error: {})",
                    e
                ));
            }
        },
        None => None,
    };

    Ok(test_threads)
}

fn get_format(
    matches: &getopts::Matches,
    quiet: bool,
    allow_unstable: bool,
) -> OptPartRes<OutputFormat> {
    let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
        None if quiet => OutputFormat::Terse,
        Some("pretty") | None => OutputFormat::Pretty,
        Some("terse") => OutputFormat::Terse,
        Some("json") => {
            if !allow_unstable {
                return Err("The \"json\" format is only accepted on the nightly compiler".into());
            }
            OutputFormat::Json
        }

        Some(v) => {
            return Err(format!(
                "argument for --format must be pretty, terse, or json (was \
                 {})",
                v
            ));
        }
    };

    Ok(format)
}

fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
        Some("auto") | None => ColorConfig::AutoColor,
        Some("always") => ColorConfig::AlwaysColor,
        Some("never") => ColorConfig::NeverColor,

        Some(v) => {
            return Err(format!(
                "argument for --color must be auto, always, or never (was \
                 {})",
                v
            ));
        }
    };

    Ok(color)
}

fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
        nocapture = match env::var("RUST_TEST_NOCAPTURE") {
            Ok(val) => &val != "0",
            Err(_) => false,
        };
    }

    Ok(nocapture)
}

fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
    let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
        (true, true) => {
            return Err("the options --include-ignored and --ignored are mutually exclusive".into());
        }
        (true, false) => RunIgnored::Yes,
        (false, true) => RunIgnored::Only,
        (false, false) => RunIgnored::No,
    };

    Ok(run_ignored)
}

fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
    let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None };

    Ok(filter)
}

fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
    let mut allow_unstable = false;

    if let Some(opt) = matches.opt_str("Z") {
        if !is_nightly() {
            return Err("the option `Z` is only accepted on the nightly compiler".into());
        }

        match &*opt {
            "unstable-options" => {
                allow_unstable = true;
            }
            _ => {
                return Err("Unrecognized option to `Z`".into());
            }
        }
    };

    Ok(allow_unstable)
}

fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
    let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));

    Ok(logfile)
}
<commit_msg>Clarify test timeout evironment variables<commit_after>//! Module converting command-line arguments into test configuration.

use getopts;
use std::env;
use std::path::PathBuf;

use super::helpers::isatty;
use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
use super::time::TestTimeOptions;

#[derive(Debug)]
pub struct TestOpts {
    pub list: bool,
    pub filter: Option<String>,
    pub filter_exact: bool,
    pub force_run_in_process: bool,
    pub exclude_should_panic: bool,
    pub run_ignored: RunIgnored,
    pub run_tests: bool,
    pub bench_benchmarks: bool,
    pub logfile: Option<PathBuf>,
    pub nocapture: bool,
    pub color: ColorConfig,
    pub format: OutputFormat,
    pub test_threads: Option<usize>,
    pub skip: Vec<String>,
    pub time_options: Option<TestTimeOptions>,
    pub options: Options,
}

impl TestOpts {
    pub fn use_color(&self) -> bool {
        match self.color {
            ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
            ColorConfig::AlwaysColor => true,
            ColorConfig::NeverColor => false,
        }
    }
}

/// Result of parsing the options.
pub type OptRes = Result<TestOpts, String>;
/// Result of parsing the option part.
type OptPartRes<T> = Result<T, String>;

fn optgroups() -> getopts::Options {
    let mut opts = getopts::Options::new();
    opts.optflag("", "include-ignored", "Run ignored and not ignored tests")
        .optflag("", "ignored", "Run only ignored tests")
        .optflag("", "force-run-in-process", "Forces tests to run in-process when panic=abort")
        .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic")
        .optflag("", "test", "Run tests and not benchmarks")
        .optflag("", "bench", "Run benchmarks instead of tests")
        .optflag("", "list", "List all tests and benchmarks")
        .optflag("h", "help", "Display this message (longer with --help)")
        .optopt(
            "",
            "logfile",
            "Write logs to the specified file instead \
             of stdout",
            "PATH",
        )
        .optflag(
            "",
            "nocapture",
            "don't capture stdout/stderr of each \
             task, allow printing directly",
        )
        .optopt(
            "",
            "test-threads",
            "Number of threads used for running tests \
             in parallel",
            "n_threads",
        )
        .optmulti(
            "",
            "skip",
            "Skip tests whose names contain FILTER (this flag can \
             be used multiple times)",
            "FILTER",
        )
        .optflag(
            "q",
            "quiet",
            "Display one character per test instead of one line. \
             Alias to --format=terse",
        )
        .optflag("", "exact", "Exactly match filters rather than by substring")
        .optopt(
            "",
            "color",
            "Configure coloring of output:
            auto   = colorize if stdout is a tty and tests are run on serially (default);
            always = always colorize output;
            never  = never colorize output;",
            "auto|always|never",
        )
        .optopt(
            "",
            "format",
            "Configure formatting of output:
            pretty = Print verbose output;
            terse  = Display one character per test;
            json   = Output a json document",
            "pretty|terse|json",
        )
        .optflag("", "show-output", "Show captured stdout of successful tests")
        .optopt(
            "Z",
            "",
            "Enable nightly-only flags:
            unstable-options = Allow use of experimental features",
            "unstable-options",
        )
        .optflagopt(
            "",
            "report-time",
            "Show execution time of each test. Awailable values:
            plain   = do not colorize the execution time (default);
            colored = colorize output according to the `color` parameter value;

            Threshold values for colorized output can be configured via
            `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
            `RUST_TEST_TIME_DOCTEST` environment variables.

            Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
            Durations must be specified in milliseconds, e.g. `500,2000` means that the warn time
            is 0.5 seconds, and the critical time is 2 seconds.

            Not available for --format=terse",
            "plain|colored",
        )
        .optflag(
            "",
            "ensure-time",
            "Treat excess of the test execution time limit as error.

            Threshold values for this option can be configured via
            `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
            `RUST_TEST_TIME_DOCTEST` environment variables.

            Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.

            `CRITICAL_TIME` here means the limit that should not be exceeded by test.
            ",
        );
    opts
}

fn usage(binary: &str, options: &getopts::Options) {
    let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
    println!(
        r#"{usage}

The FILTER string is tested against the name of all tests, and only those
tests whose names contain the filter are run.

By default, all tests are run in parallel. This can be altered with the
--test-threads flag or the RUST_TEST_THREADS environment variable when running
tests (set it to 1).

All tests have their standard output and standard error captured by default.
This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
environment variable to a value other than "0". Logging is not captured by default.

Test Attributes:

    `#[test]`        - Indicates a function is a test to be run. This function
                       takes no arguments.
    `#[bench]`       - Indicates a function is a benchmark to be run. This
                       function takes one argument (test::Bencher).
    `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
                        the code causes a panic (an assertion failure or panic!)
                        A message may be provided, which the failure string must
                        contain: #[should_panic(expected = "foo")].
    `#[ignore]`       - When applied to a function which is already attributed as a
                        test, then the test runner will ignore these tests during
                        normal test runs. Running with --ignored or --include-ignored will run
                        these tests."#,
        usage = options.usage(&message)
    );
}

/// Parses command line arguments into test options.
/// Returns `None` if help was requested (since we only show help message and don't run tests),
/// returns `Some(Err(..))` if provided arguments are incorrect,
/// otherwise creates a `TestOpts` object and returns it.
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
    // Parse matches.
    let opts = optgroups();
    let args = args.get(1..).unwrap_or(args);
    let matches = match opts.parse(args) {
        Ok(m) => m,
        Err(f) => return Some(Err(f.to_string())),
    };

    // Check if help was requested.
    if matches.opt_present("h") {
        // Show help and do nothing more.
        usage(&args[0], &opts);
        return None;
    }

    // Actually parse the opts.
    let opts_result = parse_opts_impl(matches);

    Some(opts_result)
}

// Gets the option value and checks if unstable features are enabled.
macro_rules! unstable_optflag {
    ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
        let opt = $matches.opt_present($option_name);
        if !$allow_unstable && opt {
            return Err(format!(
                "The \"{}\" flag is only accepted on the nightly compiler",
                $option_name
            ));
        }

        opt
    }};
}

// Implementation of `parse_opts` that doesn't care about help message
// and returns a `Result`.
fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
    let allow_unstable = get_allow_unstable(&matches)?;

    // Unstable flags
    let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
    let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
    let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
    let time_options = get_time_options(&matches, allow_unstable)?;

    let quiet = matches.opt_present("quiet");
    let exact = matches.opt_present("exact");
    let list = matches.opt_present("list");
    let skip = matches.opt_strs("skip");

    let bench_benchmarks = matches.opt_present("bench");
    let run_tests = !bench_benchmarks || matches.opt_present("test");

    let logfile = get_log_file(&matches)?;
    let run_ignored = get_run_ignored(&matches, include_ignored)?;
    let filter = get_filter(&matches)?;
    let nocapture = get_nocapture(&matches)?;
    let test_threads = get_test_threads(&matches)?;
    let color = get_color_config(&matches)?;
    let format = get_format(&matches, quiet, allow_unstable)?;

    let options = Options::new().display_output(matches.opt_present("show-output"));

    let test_opts = TestOpts {
        list,
        filter,
        filter_exact: exact,
        force_run_in_process,
        exclude_should_panic,
        run_ignored,
        run_tests,
        bench_benchmarks,
        logfile,
        nocapture,
        color,
        format,
        test_threads,
        skip,
        time_options,
        options,
    };

    Ok(test_opts)
}

// FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
fn is_nightly() -> bool {
    // Whether this is a feature-staged build, i.e., on the beta or stable channel
    let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
    // Whether we should enable unstable features for bootstrapping
    let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();

    bootstrap || !disable_unstable_features
}

// Gets the CLI options assotiated with `report-time` feature.
fn get_time_options(
    matches: &getopts::Matches,
    allow_unstable: bool,
) -> OptPartRes<Option<TestTimeOptions>> {
    let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
    let colored_opt_str = matches.opt_str("report-time");
    let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
    let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");

    // If `ensure-test-time` option is provided, time output is enforced,
    // so user won't be confused if any of tests will silently fail.
    let options = if report_time || ensure_test_time {
        if ensure_test_time && !report_time {
            report_time_colored = true;
        }
        Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
    } else {
        None
    };

    Ok(options)
}

fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
    let test_threads = match matches.opt_str("test-threads") {
        Some(n_str) => match n_str.parse::<usize>() {
            Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
            Ok(n) => Some(n),
            Err(e) => {
                return Err(format!(
                    "argument for --test-threads must be a number > 0 \
                     (error: {})",
                    e
                ));
            }
        },
        None => None,
    };

    Ok(test_threads)
}

fn get_format(
    matches: &getopts::Matches,
    quiet: bool,
    allow_unstable: bool,
) -> OptPartRes<OutputFormat> {
    let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
        None if quiet => OutputFormat::Terse,
        Some("pretty") | None => OutputFormat::Pretty,
        Some("terse") => OutputFormat::Terse,
        Some("json") => {
            if !allow_unstable {
                return Err("The \"json\" format is only accepted on the nightly compiler".into());
            }
            OutputFormat::Json
        }

        Some(v) => {
            return Err(format!(
                "argument for --format must be pretty, terse, or json (was \
                 {})",
                v
            ));
        }
    };

    Ok(format)
}

fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
        Some("auto") | None => ColorConfig::AutoColor,
        Some("always") => ColorConfig::AlwaysColor,
        Some("never") => ColorConfig::NeverColor,

        Some(v) => {
            return Err(format!(
                "argument for --color must be auto, always, or never (was \
                 {})",
                v
            ));
        }
    };

    Ok(color)
}

fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
        nocapture = match env::var("RUST_TEST_NOCAPTURE") {
            Ok(val) => &val != "0",
            Err(_) => false,
        };
    }

    Ok(nocapture)
}

fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
    let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
        (true, true) => {
            return Err("the options --include-ignored and --ignored are mutually exclusive".into());
        }
        (true, false) => RunIgnored::Yes,
        (false, true) => RunIgnored::Only,
        (false, false) => RunIgnored::No,
    };

    Ok(run_ignored)
}

fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
    let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None };

    Ok(filter)
}

fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
    let mut allow_unstable = false;

    if let Some(opt) = matches.opt_str("Z") {
        if !is_nightly() {
            return Err("the option `Z` is only accepted on the nightly compiler".into());
        }

        match &*opt {
            "unstable-options" => {
                allow_unstable = true;
            }
            _ => {
                return Err("Unrecognized option to `Z`".into());
            }
        }
    };

    Ok(allow_unstable)
}

fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
    let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));

    Ok(logfile)
}
<|endoftext|>


<commit_before>//! Register commands on the registry.

use std::sync::Arc;
use std::process::{Command, Stdio};
use std::thread;
use std::io::prelude::*;
use layout::commands as layout_cmds;

use registry::{self, RegistryError, RegistryGetData};
use commands::{self, CommandFn};
use layout::try_lock_tree;
use lua::{self, LuaQuery};

/// Register the default commands in the API.
///
/// Some of this code will be moved to be called after the config,
/// and will be registered dynamically.
pub fn register_defaults() {
    let mut coms = commands::write_lock();

    let mut register = |name: &'static str, val: CommandFn| {
        coms.insert(name.to_string(), val);
    };

    register("quit", Arc::new(quit));
    register("launch_terminal", Arc::new(launch_terminal));
    register("launch_dmenu", Arc::new(launch_dmenu));
    register("print_pointer", Arc::new(print_pointer));

    register("dmenu_eval", Arc::new(dmenu_eval));
    register("dmenu_lua_dofile", Arc::new(dmenu_lua_dofile));

    /// Generate switch_workspace methods and register them
    macro_rules! gen_switch_workspace {
        ( $($b:ident, $n:expr);+ ) => {
            $(fn $b() {
                trace!("Switching to workspace {}", $n);
                if let Ok(mut tree) = try_lock_tree() {
                    tree.switch_to_workspace(&$n.to_string())
                        .unwrap_or_else(|_| {
                            error!("Could not switch workspace");
                        });
                }
            }
            register(stringify!($b), Arc::new($b)); )+
        }
    }

    //// Generates move_to_workspace methods and registers them
    macro_rules! gen_move_to_workspace {
        ( $($b:ident, $n:expr);+ ) => {
            $(fn $b() {
                trace!("Switching to workspace {}", $n);
                if let Ok(mut tree) = try_lock_tree() {
                    tree.send_active_to_workspace(&$n.to_string())
                        .unwrap_or_else(|_| {
                            error!("Could not send to a different workspace");
                        })
                }
            }
              register(stringify!($b), Arc::new($b)); )+
        }
    }

    gen_switch_workspace!(switch_workspace_1, "1";
                          switch_workspace_2, "2";
                          switch_workspace_3, "3";
                          switch_workspace_4, "4";
                          switch_workspace_5, "5";
                          switch_workspace_6, "6";
                          switch_workspace_7, "7";
                          switch_workspace_8, "8";
                          switch_workspace_9, "9";
                          switch_workspace_0, "0");

    gen_move_to_workspace!(move_to_workspace_1, "1";
                           move_to_workspace_2, "2";
                           move_to_workspace_3, "3";
                           move_to_workspace_4, "4";
                           move_to_workspace_5, "5";
                           move_to_workspace_6, "6";
                           move_to_workspace_7, "7";
                           move_to_workspace_8, "8";
                           move_to_workspace_9, "9";
                           move_to_workspace_0, "0");

    register("horizontal_vertical_switch", Arc::new(layout_cmds::tile_switch));
    register("split_vertical", Arc::new(layout_cmds::split_vertical));
    register("split_horizontal", Arc::new(layout_cmds::split_horizontal));
    register("focus_left", Arc::new(layout_cmds::focus_left));
    register("focus_right", Arc::new(layout_cmds::focus_right));
    register("focus_up", Arc::new(layout_cmds::focus_up));
    register("focus_down", Arc::new(layout_cmds::focus_down));
    register("move_active_left", Arc::new(layout_cmds::move_active_left));
    register("move_active_right", Arc::new(layout_cmds::move_active_right));
    register("move_active_up", Arc::new(layout_cmds::move_active_up));
    register("move_active_down", Arc::new(layout_cmds::move_active_down));
    register("close_window", Arc::new(layout_cmds::remove_active));
    register("toggle_float_active", Arc::new(layout_cmds::toggle_float));
    register("toggle_float_focus", Arc::new(layout_cmds::toggle_float_focus));
}

// All of the methods defined should be registered.
#[deny(dead_code)]

fn launch_terminal() {
    let command = registry::get_data("terminal")
        .map(RegistryGetData::resolve).and_then(|(_, data)| {
        data.as_string().map(str::to_string)
            .ok_or(RegistryError::KeyNotFound)
    }).unwrap_or("weston_terminal".to_string());

    Command::new("sh").arg("-c")
        .arg(command)
        .spawn().expect("Error launching terminal");
}

fn launch_dmenu() {
    Command::new("sh").arg("-c")
        .arg("dmenu_run")
        .spawn().expect("Error launching terminal");
}

fn print_pointer() {
    use lua;
    use lua::LuaQuery;

    let code = "if wm == nil then print('wm table does not exist')\n\
                elseif wm.pointer == nil then print('wm.pointer table does not exist')\n\
                else\n\
                local x, y = wm.pointer.get_position()\n\
                print('The cursor is at ' .. x .. ', ' .. y)\n\
                end".to_string();
    lua::send(LuaQuery::Execute(code))
        .expect("Error telling Lua to get pointer coords");
}

fn quit() {
    info!("Closing way cooler!!");
    ::rustwlc::terminate();
}

fn dmenu_lua_dofile() {
    thread::Builder::new().name("dmenu_dofile".to_string()).spawn(|| {
        let child = Command::new("dmenu").arg("-p").arg("Eval Lua file")
            .stdin(Stdio::piped()).stdout(Stdio::piped())
            .spawn().expect("Unable to launch dmenu!");

        {
            // Write \d to stdin to prevent options from being given
            let mut stdin = child.stdin.expect("Unable to access stdin");
            stdin.write_all(b"\n").expect("Unable to write to stdin");
        }

        let mut stdout = child.stdout.expect("Unable to access stdout");
        let mut output = String::new();
        stdout.read_to_string(&mut output).expect("Unable to read stdout");

        let result = lua::send(LuaQuery::ExecFile(output))
            .expect("unable to contact Lua").recv().expect("Can't get reply");
        trace!("Lua result: {:?}", result);
    }).expect("Unable to spawn thread");
}

fn dmenu_eval() {
       thread::Builder::new().name("dmenu_eval".to_string()).spawn(|| {
           let child = Command::new("dmenu").arg("-p").arg("Eval Lua code")
               .stdin(Stdio::piped()).stdout(Stdio::piped())
               .spawn().expect("Unable to launch dmenu!");
           {
               // Write \d to stdin to prevent options from being given
               let mut stdin = child.stdin.expect("Unable to access stdin");
               stdin.write_all(b"\n").expect("Unable to write to stdin");
           }
           let mut stdout = child.stdout.expect("Unable to access stdout");
           let mut output = String::new();
           stdout.read_to_string(&mut output).expect("Unable to read stdout");

           let result = lua::send(LuaQuery::Execute(output))
               .expect("Unable to contact Lua").recv().expect("Can't get reply");
           trace!("Lua result: {:?}", result)
    }).expect("Unable to spawn thread");
}
<commit_msg>FIxed "weston-terminal" backup string<commit_after>//! Register commands on the registry.

use std::sync::Arc;
use std::process::{Command, Stdio};
use std::thread;
use std::io::prelude::*;
use layout::commands as layout_cmds;

use registry::{self, RegistryError, RegistryGetData};
use commands::{self, CommandFn};
use layout::try_lock_tree;
use lua::{self, LuaQuery};

/// Register the default commands in the API.
///
/// Some of this code will be moved to be called after the config,
/// and will be registered dynamically.
pub fn register_defaults() {
    let mut coms = commands::write_lock();

    let mut register = |name: &'static str, val: CommandFn| {
        coms.insert(name.to_string(), val);
    };

    register("quit", Arc::new(quit));
    register("launch_terminal", Arc::new(launch_terminal));
    register("launch_dmenu", Arc::new(launch_dmenu));
    register("print_pointer", Arc::new(print_pointer));

    register("dmenu_eval", Arc::new(dmenu_eval));
    register("dmenu_lua_dofile", Arc::new(dmenu_lua_dofile));

    /// Generate switch_workspace methods and register them
    macro_rules! gen_switch_workspace {
        ( $($b:ident, $n:expr);+ ) => {
            $(fn $b() {
                trace!("Switching to workspace {}", $n);
                if let Ok(mut tree) = try_lock_tree() {
                    tree.switch_to_workspace(&$n.to_string())
                        .unwrap_or_else(|_| {
                            error!("Could not switch workspace");
                        });
                }
            }
            register(stringify!($b), Arc::new($b)); )+
        }
    }

    //// Generates move_to_workspace methods and registers them
    macro_rules! gen_move_to_workspace {
        ( $($b:ident, $n:expr);+ ) => {
            $(fn $b() {
                trace!("Switching to workspace {}", $n);
                if let Ok(mut tree) = try_lock_tree() {
                    tree.send_active_to_workspace(&$n.to_string())
                        .unwrap_or_else(|_| {
                            error!("Could not send to a different workspace");
                        })
                }
            }
              register(stringify!($b), Arc::new($b)); )+
        }
    }

    gen_switch_workspace!(switch_workspace_1, "1";
                          switch_workspace_2, "2";
                          switch_workspace_3, "3";
                          switch_workspace_4, "4";
                          switch_workspace_5, "5";
                          switch_workspace_6, "6";
                          switch_workspace_7, "7";
                          switch_workspace_8, "8";
                          switch_workspace_9, "9";
                          switch_workspace_0, "0");

    gen_move_to_workspace!(move_to_workspace_1, "1";
                           move_to_workspace_2, "2";
                           move_to_workspace_3, "3";
                           move_to_workspace_4, "4";
                           move_to_workspace_5, "5";
                           move_to_workspace_6, "6";
                           move_to_workspace_7, "7";
                           move_to_workspace_8, "8";
                           move_to_workspace_9, "9";
                           move_to_workspace_0, "0");

    register("horizontal_vertical_switch", Arc::new(layout_cmds::tile_switch));
    register("split_vertical", Arc::new(layout_cmds::split_vertical));
    register("split_horizontal", Arc::new(layout_cmds::split_horizontal));
    register("focus_left", Arc::new(layout_cmds::focus_left));
    register("focus_right", Arc::new(layout_cmds::focus_right));
    register("focus_up", Arc::new(layout_cmds::focus_up));
    register("focus_down", Arc::new(layout_cmds::focus_down));
    register("move_active_left", Arc::new(layout_cmds::move_active_left));
    register("move_active_right", Arc::new(layout_cmds::move_active_right));
    register("move_active_up", Arc::new(layout_cmds::move_active_up));
    register("move_active_down", Arc::new(layout_cmds::move_active_down));
    register("close_window", Arc::new(layout_cmds::remove_active));
    register("toggle_float_active", Arc::new(layout_cmds::toggle_float));
    register("toggle_float_focus", Arc::new(layout_cmds::toggle_float_focus));
}

// All of the methods defined should be registered.
#[deny(dead_code)]

fn launch_terminal() {
    let command = registry::get_data("terminal")
        .map(RegistryGetData::resolve).and_then(|(_, data)| {
        data.as_string().map(str::to_string)
            .ok_or(RegistryError::KeyNotFound)
    }).unwrap_or("weston-terminal".to_string());

    Command::new("sh").arg("-c")
        .arg(command)
        .spawn().expect("Error launching terminal");
}

fn launch_dmenu() {
    Command::new("sh").arg("-c")
        .arg("dmenu_run")
        .spawn().expect("Error launching terminal");
}

fn print_pointer() {
    use lua;
    use lua::LuaQuery;

    let code = "if wm == nil then print('wm table does not exist')\n\
                elseif wm.pointer == nil then print('wm.pointer table does not exist')\n\
                else\n\
                local x, y = wm.pointer.get_position()\n\
                print('The cursor is at ' .. x .. ', ' .. y)\n\
                end".to_string();
    lua::send(LuaQuery::Execute(code))
        .expect("Error telling Lua to get pointer coords");
}

fn quit() {
    info!("Closing way cooler!!");
    ::rustwlc::terminate();
}

fn dmenu_lua_dofile() {
    thread::Builder::new().name("dmenu_dofile".to_string()).spawn(|| {
        let child = Command::new("dmenu").arg("-p").arg("Eval Lua file")
            .stdin(Stdio::piped()).stdout(Stdio::piped())
            .spawn().expect("Unable to launch dmenu!");

        {
            // Write \d to stdin to prevent options from being given
            let mut stdin = child.stdin.expect("Unable to access stdin");
            stdin.write_all(b"\n").expect("Unable to write to stdin");
        }

        let mut stdout = child.stdout.expect("Unable to access stdout");
        let mut output = String::new();
        stdout.read_to_string(&mut output).expect("Unable to read stdout");

        let result = lua::send(LuaQuery::ExecFile(output))
            .expect("unable to contact Lua").recv().expect("Can't get reply");
        trace!("Lua result: {:?}", result);
    }).expect("Unable to spawn thread");
}

fn dmenu_eval() {
       thread::Builder::new().name("dmenu_eval".to_string()).spawn(|| {
           let child = Command::new("dmenu").arg("-p").arg("Eval Lua code")
               .stdin(Stdio::piped()).stdout(Stdio::piped())
               .spawn().expect("Unable to launch dmenu!");
           {
               // Write \d to stdin to prevent options from being given
               let mut stdin = child.stdin.expect("Unable to access stdin");
               stdin.write_all(b"\n").expect("Unable to write to stdin");
           }
           let mut stdout = child.stdout.expect("Unable to access stdout");
           let mut output = String::new();
           stdout.read_to_string(&mut output).expect("Unable to read stdout");

           let result = lua::send(LuaQuery::Execute(output))
               .expect("Unable to contact Lua").recv().expect("Can't get reply");
           trace!("Lua result: {:?}", result)
    }).expect("Unable to spawn thread");
}
<|endoftext|>


<commit_before>use alga::general::RealField;
use crate::math::Isometry;
use crate::pipeline::broad_phase::BroadPhaseProxyHandle;
use crate::pipeline::narrow_phase::CollisionObjectGraphIndex;
use crate::pipeline::object::{CollisionGroups, GeometricQueryType};
use crate::query::ContactPrediction;
use crate::shape::Shape;
use crate::bounding_volume::{self, BoundingVolume, AABB};

use slab::{Iter, Slab};
use std::ops::{Index, IndexMut};
use crate::pipeline::object::{CollisionObject, CollisionObjectHandle, CollisionObjectRef};

pub trait CollisionObjectSet<'a, N: RealField> {
    type CollisionObject: CollisionObjectRef<'a, N, Handle = Self::Handle>;
    type CollisionObjects: Iterator<Item = (Self::Handle, Self::CollisionObject)>;
    type Handle: Copy;

    fn get(&'a self, handle: Self::Handle) -> Option<Self::CollisionObject>;
    fn contains(&self, handle: Self::Handle) -> bool;
    fn iter(&'a self) -> Self::CollisionObjects;
}

impl<'a, N: RealField, T: 'a> CollisionObjectSet<'a, N> for CollisionObjectSlab<N, T> {
    type CollisionObject = &'a CollisionObject<N, T>;
    type CollisionObjects = CollisionObjects<'a, N, T>;
    type Handle = CollisionObjectHandle;

    fn get(&'a self, handle: Self::Handle) -> Option<Self::CollisionObject> {
        self.get(handle)
    }

    fn contains(&self, handle: Self::Handle) -> bool {
        self.contains(handle)
    }

    fn iter(&'a self) -> Self::CollisionObjects {
        CollisionObjects {
            iter: self.objects.iter()
        }
    }
}

/// A set of collision objects that can be indexed by collision object handles.
pub struct CollisionObjectSlab<N: RealField, T> {
    pub(crate) objects: Slab<CollisionObject<N, T>>,
}

impl<N: RealField, T> CollisionObjectSlab<N, T> {
    /// Creates a new empty collection of collision objects.
    pub fn new() -> CollisionObjectSlab<N, T> {
        CollisionObjectSlab {
            objects: Slab::new(),
        }
    }

    /// Inserts a new collision object into this collection and returns the corresponding handle.
    #[inline]
    pub fn insert(&mut self, co: CollisionObject<N, T>) -> CollisionObjectHandle {
        CollisionObjectHandle(self.objects.insert(co))
    }

    /// Removes from this collection the collision object identified by the given handle.
    ///
    /// The removed collision object structure is returned.
    #[inline]
    pub fn remove(&mut self, handle: CollisionObjectHandle) -> CollisionObject<N, T> {
        self.objects.remove(handle.0)
    }

    /// If it exists, retrieves a reference to the collision object identified by the given handle.
    #[inline]
    pub fn get(&self, handle: CollisionObjectHandle) -> Option<&CollisionObject<N, T>> {
        self.objects.get(handle.0)
    }

    /// If it exists, retrieves a mutable reference to the collision object identified by the given handle.
    #[inline]
    pub fn get_mut(&mut self, handle: CollisionObjectHandle) -> Option<&mut CollisionObject<N, T>> {
        self.objects.get_mut(handle.0)
    }

    /// If they exists, retrieves a mutable reference to the two collision object identified by the given handles.
    ///
    /// Panics if both handles are equal.
    #[inline]
    pub fn get_pair_mut(&mut self,
                        handle1: CollisionObjectHandle,
                        handle2: CollisionObjectHandle)
                        -> (Option<&mut CollisionObject<N, T>>, Option<&mut CollisionObject<N, T>>) {
        assert_ne!(handle1, handle2, "The two handles must not be the same.");
        let a = self.objects.get_mut(handle1.0).map(|o| o as *mut _);
        (a.map(|a| unsafe { std::mem::transmute(a) }), self.objects.get_mut(handle2.0))
    }

    /// Returns `true` if the specified handle identifies a collision object stored in this collection.
    #[inline]
    pub fn contains(&self, handle: CollisionObjectHandle) -> bool {
        self.objects.contains(handle.0)
    }

    /// Retrieves an iterator yielding references to each collision object.
    #[inline]
    pub fn iter(&self) -> CollisionObjects<N, T> {
        CollisionObjects {
            iter: self.objects.iter(),
        }
    }

    /// The number of collision objects on this slab.
    #[inline]
    pub fn len(&self) -> usize {
        self.objects.len()
    }
}

impl<N: RealField, T> Index<CollisionObjectHandle> for CollisionObjectSlab<N, T> {
    type Output = CollisionObject<N, T>;

    #[inline]
    fn index(&self, handle: CollisionObjectHandle) -> &Self::Output {
        &self.objects[handle.0]
    }
}

impl<N: RealField, T> IndexMut<CollisionObjectHandle> for CollisionObjectSlab<N, T> {
    #[inline]
    fn index_mut(&mut self, handle: CollisionObjectHandle) -> &mut Self::Output {
        &mut self.objects[handle.0]
    }
}

/// An iterator yielding references to collision objects.
pub struct CollisionObjects<'a, N: 'a + RealField, T: 'a> {
    iter: Iter<'a, CollisionObject<N, T>>,
}

impl<'a, N: 'a + RealField, T: 'a> Iterator for CollisionObjects<'a, N, T> {
    type Item = (CollisionObjectHandle, &'a CollisionObject<N, T>);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|obj| ((CollisionObjectHandle(obj.0), obj.1)))
    }
}<commit_msg>Use better names for CollisionObjectSet associated iterators.<commit_after>use alga::general::RealField;
use crate::math::Isometry;
use crate::pipeline::broad_phase::BroadPhaseProxyHandle;
use crate::pipeline::narrow_phase::CollisionObjectGraphIndex;
use crate::pipeline::object::{CollisionGroups, GeometricQueryType};
use crate::query::ContactPrediction;
use crate::shape::Shape;
use crate::bounding_volume::{self, BoundingVolume, AABB};

use slab::{Iter, Slab};
use std::ops::{Index, IndexMut};
use crate::pipeline::object::{CollisionObject, CollisionObjectHandle, CollisionObjectRef};

pub trait CollisionObjectSet<'a, N: RealField> {
    type CollisionObject: CollisionObjectRef<'a, N, Handle = Self::Handle>;
    type Iter: Iterator<Item = (Self::Handle, Self::CollisionObject)>;
    type Handle: Copy;

    fn get(&'a self, handle: Self::Handle) -> Option<Self::CollisionObject>;
    fn contains(&self, handle: Self::Handle) -> bool;
    fn iter(&'a self) -> Self::Iter;
}

impl<'a, N: RealField, T: 'a> CollisionObjectSet<'a, N> for CollisionObjectSlab<N, T> {
    type CollisionObject = &'a CollisionObject<N, T>;
    type Iter = CollisionObjects<'a, N, T>;
    type Handle = CollisionObjectHandle;

    fn get(&'a self, handle: Self::Handle) -> Option<Self::CollisionObject> {
        self.get(handle)
    }

    fn contains(&self, handle: Self::Handle) -> bool {
        self.contains(handle)
    }

    fn iter(&'a self) -> Self::Iter {
        CollisionObjects {
            iter: self.objects.iter()
        }
    }
}

/// A set of collision objects that can be indexed by collision object handles.
pub struct CollisionObjectSlab<N: RealField, T> {
    pub(crate) objects: Slab<CollisionObject<N, T>>,
}

impl<N: RealField, T> CollisionObjectSlab<N, T> {
    /// Creates a new empty collection of collision objects.
    pub fn new() -> CollisionObjectSlab<N, T> {
        CollisionObjectSlab {
            objects: Slab::new(),
        }
    }

    /// Inserts a new collision object into this collection and returns the corresponding handle.
    #[inline]
    pub fn insert(&mut self, co: CollisionObject<N, T>) -> CollisionObjectHandle {
        CollisionObjectHandle(self.objects.insert(co))
    }

    /// Removes from this collection the collision object identified by the given handle.
    ///
    /// The removed collision object structure is returned.
    #[inline]
    pub fn remove(&mut self, handle: CollisionObjectHandle) -> CollisionObject<N, T> {
        self.objects.remove(handle.0)
    }

    /// If it exists, retrieves a reference to the collision object identified by the given handle.
    #[inline]
    pub fn get(&self, handle: CollisionObjectHandle) -> Option<&CollisionObject<N, T>> {
        self.objects.get(handle.0)
    }

    /// If it exists, retrieves a mutable reference to the collision object identified by the given handle.
    #[inline]
    pub fn get_mut(&mut self, handle: CollisionObjectHandle) -> Option<&mut CollisionObject<N, T>> {
        self.objects.get_mut(handle.0)
    }

    /// If they exists, retrieves a mutable reference to the two collision object identified by the given handles.
    ///
    /// Panics if both handles are equal.
    #[inline]
    pub fn get_pair_mut(&mut self,
                        handle1: CollisionObjectHandle,
                        handle2: CollisionObjectHandle)
                        -> (Option<&mut CollisionObject<N, T>>, Option<&mut CollisionObject<N, T>>) {
        assert_ne!(handle1, handle2, "The two handles must not be the same.");
        let a = self.objects.get_mut(handle1.0).map(|o| o as *mut _);
        (a.map(|a| unsafe { std::mem::transmute(a) }), self.objects.get_mut(handle2.0))
    }

    /// Returns `true` if the specified handle identifies a collision object stored in this collection.
    #[inline]
    pub fn contains(&self, handle: CollisionObjectHandle) -> bool {
        self.objects.contains(handle.0)
    }

    /// Retrieves an iterator yielding references to each collision object.
    #[inline]
    pub fn iter(&self) -> CollisionObjects<N, T> {
        CollisionObjects {
            iter: self.objects.iter(),
        }
    }

    /// The number of collision objects on this slab.
    #[inline]
    pub fn len(&self) -> usize {
        self.objects.len()
    }
}

impl<N: RealField, T> Index<CollisionObjectHandle> for CollisionObjectSlab<N, T> {
    type Output = CollisionObject<N, T>;

    #[inline]
    fn index(&self, handle: CollisionObjectHandle) -> &Self::Output {
        &self.objects[handle.0]
    }
}

impl<N: RealField, T> IndexMut<CollisionObjectHandle> for CollisionObjectSlab<N, T> {
    #[inline]
    fn index_mut(&mut self, handle: CollisionObjectHandle) -> &mut Self::Output {
        &mut self.objects[handle.0]
    }
}

/// An iterator yielding references to collision objects.
pub struct CollisionObjects<'a, N: 'a + RealField, T: 'a> {
    iter: Iter<'a, CollisionObject<N, T>>,
}

impl<'a, N: 'a + RealField, T: 'a> Iterator for CollisionObjects<'a, N, T> {
    type Item = (CollisionObjectHandle, &'a CollisionObject<N, T>);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|obj| ((CollisionObjectHandle(obj.0), obj.1)))
    }
}<|endoftext|>


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

//! Seeds a server with initial peers on first start and keep monitoring
//! peer counts to connect to more if neeed. Seedin strategy is
//! configurable with either no peers, a user-defined list or a preset
//! list of DNS records (the default).

use chrono::prelude::Utc;
use chrono::{Duration, MIN_DATE};
use rand::{thread_rng, Rng};
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::{mpsc, Arc};
use std::{cmp, str, thread, time};

use crate::core::global;
use crate::p2p;
use crate::p2p::ChainAdapter;
use crate::pool::DandelionConfig;
use crate::util::{Mutex, StopState};

// DNS Seeds with contact email associated
const MAINNET_DNS_SEEDS: &'static [&'static str] = &[
	"mainnet.seed.grin-tech.org", // igno.peverell@protonmail.com
	"mainnet.seed.grin.icu",      // gary.peverell@protonmail.com
	"mainnet.seed.713.mw",        // jasper@713.mw
];
const FLOONET_DNS_SEEDS: &'static [&'static str] = &[
	"floonet.seed.grin-tech.org", // igno.peverell@protonmail.com
	"floonet.seed.grin.icu",      // gary.peverell@protonmail.com
	"floonet.seed.713.mw",        // jasper@713.mw
];

pub fn connect_and_monitor(
	p2p_server: Arc<p2p::Server>,
	capabilities: p2p::Capabilities,
	dandelion_config: DandelionConfig,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr> + Send>,
	preferred_peers: Option<Vec<SocketAddr>>,
	stop_state: Arc<Mutex<StopState>>,
) {
	let _ = thread::Builder::new()
		.name("seed".to_string())
		.spawn(move || {
			let peers = p2p_server.peers.clone();

			// open a channel with a listener that connects every peer address sent below
			// max peer count
			let (tx, rx) = mpsc::channel();

			// check seeds first
			connect_to_seeds_and_preferred_peers(
				peers.clone(),
				tx.clone(),
				seed_list,
				preferred_peers.clone(),
			);

			let mut prev = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_expire_check = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_ping = Utc::now();
			let mut start_attempt = 0;

			loop {
				if stop_state.lock().is_stopped() {
					break;
				}

				// Pause egress peer connection request. Only for tests.
				if stop_state.lock().is_paused() {
					thread::sleep(time::Duration::from_secs(1));
					continue;
				}

				// Check for and remove expired peers from the storage
				if Utc::now() - prev_expire_check > Duration::hours(1) {
					peers.remove_expired();

					prev_expire_check = Utc::now();
				}

				// make several attempts to get peers as quick as possible
				// with exponential backoff
				if Utc::now() - prev > Duration::seconds(cmp::min(20, 1 << start_attempt)) {
					// try to connect to any address sent to the channel
					listen_for_addrs(peers.clone(), p2p_server.clone(), capabilities, &rx);

					// monitor additional peers if we need to add more
					monitor_peers(
						peers.clone(),
						p2p_server.config.clone(),
						tx.clone(),
						preferred_peers.clone(),
					);

					update_dandelion_relay(peers.clone(), dandelion_config.clone());

					prev = Utc::now();
					start_attempt = cmp::min(6, start_attempt + 1);
				}

				// Ping connected peers on every 10s to monitor peers.
				if Utc::now() - prev_ping > Duration::seconds(10) {
					let total_diff = peers.total_difficulty();
					let total_height = peers.total_height();
					peers.check_all(total_diff, total_height);
					prev_ping = Utc::now();
				}

				thread::sleep(time::Duration::from_secs(1));
			}
		});
}

fn monitor_peers(
	peers: Arc<p2p::Peers>,
	config: p2p::P2PConfig,
	tx: mpsc::Sender<SocketAddr>,
	preferred_peers_list: Option<Vec<SocketAddr>>,
) {
	// regularly check if we need to acquire more peers  and if so, gets
	// them from db
	let total_count = peers.all_peers().len();
	let mut healthy_count = 0;
	let mut banned_count = 0;
	let mut defuncts = vec![];

	for x in peers.all_peers() {
		match x.flags {
			p2p::State::Banned => {
				let interval = Utc::now().timestamp() - x.last_banned;
				// Unban peer
				if interval >= config.ban_window() {
					peers.unban_peer(&x.addr);
					debug!(
						"monitor_peers: unbanned {} after {} seconds",
						x.addr, interval
					);
				} else {
					banned_count += 1;
				}
			}
			p2p::State::Healthy => healthy_count += 1,
			p2p::State::Defunct => defuncts.push(x),
		}
	}

	debug!(
		"monitor_peers: on {}:{}, {} connected ({} most_work). \
		 all {} = {} healthy + {} banned + {} defunct",
		config.host,
		config.port,
		peers.peer_count(),
		peers.most_work_peers().len(),
		total_count,
		healthy_count,
		banned_count,
		defuncts.len(),
	);

	// maintenance step first, clean up p2p server peers
	peers.clean_peers(config.peer_max_count() as usize);

	// not enough peers, getting more from db
	if peers.peer_count() >= config.peer_min_preferred_count() {
		return;
	}

	// loop over connected peers
	// ask them for their list of peers
	let mut connected_peers: Vec<SocketAddr> = vec![];
	for p in peers.connected_peers() {
		trace!(
			"monitor_peers: {}:{} ask {} for more peers",
			config.host,
			config.port,
			p.info.addr,
		);
		let _ = p.send_peer_request(p2p::Capabilities::PEER_LIST);
		connected_peers.push(p.info.addr)
	}

	// Attempt to connect to preferred peers if there is some
	match preferred_peers_list {
		Some(preferred_peers) => {
			for p in preferred_peers {
				if !connected_peers.is_empty() {
					if !connected_peers.contains(&p) {
						tx.send(p).unwrap();
					}
				} else {
					tx.send(p).unwrap();
				}
			}
		}
		None => debug!("monitor_peers: no preferred peers"),
	}

	// take a random defunct peer and mark it healthy: over a long period any
	// peer will see another as defunct eventually, gives us a chance to retry
	if defuncts.len() > 0 {
		thread_rng().shuffle(&mut defuncts);
		let _ = peers.update_state(defuncts[0].addr, p2p::State::Healthy);
	}

	// find some peers from our db
	// and queue them up for a connection attempt
	let new_peers = peers.find_peers(
		p2p::State::Healthy,
		p2p::Capabilities::UNKNOWN,
		config.peer_max_count() as usize,
	);

	for p in new_peers.iter().filter(|p| !peers.is_known(&p.addr)) {
		trace!(
			"monitor_peers: on {}:{}, queue to soon try {}",
			config.host,
			config.port,
			p.addr,
		);
		tx.send(p.addr).unwrap();
	}
}

fn update_dandelion_relay(peers: Arc<p2p::Peers>, dandelion_config: DandelionConfig) {
	// Dandelion Relay Updater
	let dandelion_relay = peers.get_dandelion_relay();
	if dandelion_relay.is_empty() {
		debug!("monitor_peers: no dandelion relay updating");
		peers.update_dandelion_relay();
	} else {
		for last_added in dandelion_relay.keys() {
			let dandelion_interval = Utc::now().timestamp() - last_added;
			if dandelion_interval >= dandelion_config.relay_secs.unwrap() as i64 {
				debug!("monitor_peers: updating expired dandelion relay");
				peers.update_dandelion_relay();
			}
		}
	}
}

// Check if we have any pre-existing peer in db. If so, start with those,
// otherwise use the seeds provided.
fn connect_to_seeds_and_preferred_peers(
	peers: Arc<p2p::Peers>,
	tx: mpsc::Sender<SocketAddr>,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr>>,
	peers_preferred_list: Option<Vec<SocketAddr>>,
) {
	// check if we have some peers in db
	// look for peers that are able to give us other peers (via PEER_LIST capability)
	let peers = peers.find_peers(p2p::State::Healthy, p2p::Capabilities::PEER_LIST, 100);

	// if so, get their addresses, otherwise use our seeds
	let mut peer_addrs = if peers.len() > 3 {
		peers.iter().map(|p| p.addr).collect::<Vec<_>>()
	} else {
		seed_list()
	};

	// If we have preferred peers add them to the connection
	match peers_preferred_list {
		Some(mut peers_preferred) => peer_addrs.append(&mut peers_preferred),
		None => trace!("No preferred peers"),
	};

	if peer_addrs.len() == 0 {
		warn!("No seeds were retrieved.");
	}

	// connect to this first set of addresses
	for addr in peer_addrs {
		tx.send(addr).unwrap();
	}
}

/// Regularly poll a channel receiver for new addresses and initiate a
/// connection if the max peer count isn't exceeded. A request for more
/// peers is also automatically sent after connection.
fn listen_for_addrs(
	peers: Arc<p2p::Peers>,
	p2p: Arc<p2p::Server>,
	capab: p2p::Capabilities,
	rx: &mpsc::Receiver<SocketAddr>,
) {
	if peers.peer_count() >= p2p.config.peer_max_count() {
		// clean the rx messages to avoid accumulating
		for _ in rx.try_iter() {}
		return;
	}

	for addr in rx.try_iter() {
		let peers_c = peers.clone();
		let p2p_c = p2p.clone();
		let _ = thread::Builder::new()
			.name("peer_connect".to_string())
			.spawn(move || match p2p_c.connect(&addr) {
				Ok(p) => {
					let _ = p.send_peer_request(capab);
					let _ = peers_c.update_state(addr, p2p::State::Healthy);
				}
				Err(_) => {
					let _ = peers_c.update_state(addr, p2p::State::Defunct);
				}
			});
	}
}

pub fn dns_seeds() -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let mut addresses: Vec<SocketAddr> = vec![];
		let net_seeds = if global::is_floonet() {
			FLOONET_DNS_SEEDS
		} else {
			MAINNET_DNS_SEEDS
		};
		for dns_seed in net_seeds {
			let temp_addresses = addresses.clone();
			debug!("Retrieving seed nodes from dns {}", dns_seed);
			match (dns_seed.to_owned(), 0).to_socket_addrs() {
				Ok(addrs) => addresses.append(
					&mut (addrs
						.map(|mut addr| {
							addr.set_port(13414);
							addr
						})
						.filter(|addr| !temp_addresses.contains(addr))
						.collect()),
				),
				Err(e) => debug!("Failed to resolve seed {:?} got error {:?}", dns_seed, e),
			}
		}
		debug!("Retrieved seed addresses: {:?}", addresses);
		addresses
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>) -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(move || {
		addrs_str
			.iter()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>()
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn preferred_peers(addrs_str: Vec<String>) -> Option<Vec<SocketAddr>> {
	if addrs_str.is_empty() {
		None
	} else {
		Some(
			addrs_str
				.iter()
				.map(|s| s.parse().unwrap())
				.collect::<Vec<_>>(),
		)
	}
}
<commit_msg>Add Quentin Le Sceller DNS Seed (#2302)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Seeds a server with initial peers on first start and keep monitoring
//! peer counts to connect to more if neeed. Seedin strategy is
//! configurable with either no peers, a user-defined list or a preset
//! list of DNS records (the default).

use chrono::prelude::Utc;
use chrono::{Duration, MIN_DATE};
use rand::{thread_rng, Rng};
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::{mpsc, Arc};
use std::{cmp, str, thread, time};

use crate::core::global;
use crate::p2p;
use crate::p2p::ChainAdapter;
use crate::pool::DandelionConfig;
use crate::util::{Mutex, StopState};

// DNS Seeds with contact email associated
const MAINNET_DNS_SEEDS: &'static [&'static str] = &[
	"mainnet.seed.grin-tech.org",      // igno.peverell@protonmail.com
	"mainnet.seed.grin.icu",           // gary.peverell@protonmail.com
	"mainnet.seed.713.mw",             // jasper@713.mw
	"mainnet.seed.grin.lesceller.com", // q.lesceller@gmail.com
];
const FLOONET_DNS_SEEDS: &'static [&'static str] = &[
	"floonet.seed.grin-tech.org",      // igno.peverell@protonmail.com
	"floonet.seed.grin.icu",           // gary.peverell@protonmail.com
	"floonet.seed.713.mw",             // jasper@713.mw
	"floonet.seed.grin.lesceller.com", // q.lesceller@gmail.com
];

pub fn connect_and_monitor(
	p2p_server: Arc<p2p::Server>,
	capabilities: p2p::Capabilities,
	dandelion_config: DandelionConfig,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr> + Send>,
	preferred_peers: Option<Vec<SocketAddr>>,
	stop_state: Arc<Mutex<StopState>>,
) {
	let _ = thread::Builder::new()
		.name("seed".to_string())
		.spawn(move || {
			let peers = p2p_server.peers.clone();

			// open a channel with a listener that connects every peer address sent below
			// max peer count
			let (tx, rx) = mpsc::channel();

			// check seeds first
			connect_to_seeds_and_preferred_peers(
				peers.clone(),
				tx.clone(),
				seed_list,
				preferred_peers.clone(),
			);

			let mut prev = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_expire_check = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_ping = Utc::now();
			let mut start_attempt = 0;

			loop {
				if stop_state.lock().is_stopped() {
					break;
				}

				// Pause egress peer connection request. Only for tests.
				if stop_state.lock().is_paused() {
					thread::sleep(time::Duration::from_secs(1));
					continue;
				}

				// Check for and remove expired peers from the storage
				if Utc::now() - prev_expire_check > Duration::hours(1) {
					peers.remove_expired();

					prev_expire_check = Utc::now();
				}

				// make several attempts to get peers as quick as possible
				// with exponential backoff
				if Utc::now() - prev > Duration::seconds(cmp::min(20, 1 << start_attempt)) {
					// try to connect to any address sent to the channel
					listen_for_addrs(peers.clone(), p2p_server.clone(), capabilities, &rx);

					// monitor additional peers if we need to add more
					monitor_peers(
						peers.clone(),
						p2p_server.config.clone(),
						tx.clone(),
						preferred_peers.clone(),
					);

					update_dandelion_relay(peers.clone(), dandelion_config.clone());

					prev = Utc::now();
					start_attempt = cmp::min(6, start_attempt + 1);
				}

				// Ping connected peers on every 10s to monitor peers.
				if Utc::now() - prev_ping > Duration::seconds(10) {
					let total_diff = peers.total_difficulty();
					let total_height = peers.total_height();
					peers.check_all(total_diff, total_height);
					prev_ping = Utc::now();
				}

				thread::sleep(time::Duration::from_secs(1));
			}
		});
}

fn monitor_peers(
	peers: Arc<p2p::Peers>,
	config: p2p::P2PConfig,
	tx: mpsc::Sender<SocketAddr>,
	preferred_peers_list: Option<Vec<SocketAddr>>,
) {
	// regularly check if we need to acquire more peers  and if so, gets
	// them from db
	let total_count = peers.all_peers().len();
	let mut healthy_count = 0;
	let mut banned_count = 0;
	let mut defuncts = vec![];

	for x in peers.all_peers() {
		match x.flags {
			p2p::State::Banned => {
				let interval = Utc::now().timestamp() - x.last_banned;
				// Unban peer
				if interval >= config.ban_window() {
					peers.unban_peer(&x.addr);
					debug!(
						"monitor_peers: unbanned {} after {} seconds",
						x.addr, interval
					);
				} else {
					banned_count += 1;
				}
			}
			p2p::State::Healthy => healthy_count += 1,
			p2p::State::Defunct => defuncts.push(x),
		}
	}

	debug!(
		"monitor_peers: on {}:{}, {} connected ({} most_work). \
		 all {} = {} healthy + {} banned + {} defunct",
		config.host,
		config.port,
		peers.peer_count(),
		peers.most_work_peers().len(),
		total_count,
		healthy_count,
		banned_count,
		defuncts.len(),
	);

	// maintenance step first, clean up p2p server peers
	peers.clean_peers(config.peer_max_count() as usize);

	// not enough peers, getting more from db
	if peers.peer_count() >= config.peer_min_preferred_count() {
		return;
	}

	// loop over connected peers
	// ask them for their list of peers
	let mut connected_peers: Vec<SocketAddr> = vec![];
	for p in peers.connected_peers() {
		trace!(
			"monitor_peers: {}:{} ask {} for more peers",
			config.host,
			config.port,
			p.info.addr,
		);
		let _ = p.send_peer_request(p2p::Capabilities::PEER_LIST);
		connected_peers.push(p.info.addr)
	}

	// Attempt to connect to preferred peers if there is some
	match preferred_peers_list {
		Some(preferred_peers) => {
			for p in preferred_peers {
				if !connected_peers.is_empty() {
					if !connected_peers.contains(&p) {
						tx.send(p).unwrap();
					}
				} else {
					tx.send(p).unwrap();
				}
			}
		}
		None => debug!("monitor_peers: no preferred peers"),
	}

	// take a random defunct peer and mark it healthy: over a long period any
	// peer will see another as defunct eventually, gives us a chance to retry
	if defuncts.len() > 0 {
		thread_rng().shuffle(&mut defuncts);
		let _ = peers.update_state(defuncts[0].addr, p2p::State::Healthy);
	}

	// find some peers from our db
	// and queue them up for a connection attempt
	let new_peers = peers.find_peers(
		p2p::State::Healthy,
		p2p::Capabilities::UNKNOWN,
		config.peer_max_count() as usize,
	);

	for p in new_peers.iter().filter(|p| !peers.is_known(&p.addr)) {
		trace!(
			"monitor_peers: on {}:{}, queue to soon try {}",
			config.host,
			config.port,
			p.addr,
		);
		tx.send(p.addr).unwrap();
	}
}

fn update_dandelion_relay(peers: Arc<p2p::Peers>, dandelion_config: DandelionConfig) {
	// Dandelion Relay Updater
	let dandelion_relay = peers.get_dandelion_relay();
	if dandelion_relay.is_empty() {
		debug!("monitor_peers: no dandelion relay updating");
		peers.update_dandelion_relay();
	} else {
		for last_added in dandelion_relay.keys() {
			let dandelion_interval = Utc::now().timestamp() - last_added;
			if dandelion_interval >= dandelion_config.relay_secs.unwrap() as i64 {
				debug!("monitor_peers: updating expired dandelion relay");
				peers.update_dandelion_relay();
			}
		}
	}
}

// Check if we have any pre-existing peer in db. If so, start with those,
// otherwise use the seeds provided.
fn connect_to_seeds_and_preferred_peers(
	peers: Arc<p2p::Peers>,
	tx: mpsc::Sender<SocketAddr>,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr>>,
	peers_preferred_list: Option<Vec<SocketAddr>>,
) {
	// check if we have some peers in db
	// look for peers that are able to give us other peers (via PEER_LIST capability)
	let peers = peers.find_peers(p2p::State::Healthy, p2p::Capabilities::PEER_LIST, 100);

	// if so, get their addresses, otherwise use our seeds
	let mut peer_addrs = if peers.len() > 3 {
		peers.iter().map(|p| p.addr).collect::<Vec<_>>()
	} else {
		seed_list()
	};

	// If we have preferred peers add them to the connection
	match peers_preferred_list {
		Some(mut peers_preferred) => peer_addrs.append(&mut peers_preferred),
		None => trace!("No preferred peers"),
	};

	if peer_addrs.len() == 0 {
		warn!("No seeds were retrieved.");
	}

	// connect to this first set of addresses
	for addr in peer_addrs {
		tx.send(addr).unwrap();
	}
}

/// Regularly poll a channel receiver for new addresses and initiate a
/// connection if the max peer count isn't exceeded. A request for more
/// peers is also automatically sent after connection.
fn listen_for_addrs(
	peers: Arc<p2p::Peers>,
	p2p: Arc<p2p::Server>,
	capab: p2p::Capabilities,
	rx: &mpsc::Receiver<SocketAddr>,
) {
	if peers.peer_count() >= p2p.config.peer_max_count() {
		// clean the rx messages to avoid accumulating
		for _ in rx.try_iter() {}
		return;
	}

	for addr in rx.try_iter() {
		let peers_c = peers.clone();
		let p2p_c = p2p.clone();
		let _ = thread::Builder::new()
			.name("peer_connect".to_string())
			.spawn(move || match p2p_c.connect(&addr) {
				Ok(p) => {
					let _ = p.send_peer_request(capab);
					let _ = peers_c.update_state(addr, p2p::State::Healthy);
				}
				Err(_) => {
					let _ = peers_c.update_state(addr, p2p::State::Defunct);
				}
			});
	}
}

pub fn dns_seeds() -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let mut addresses: Vec<SocketAddr> = vec![];
		let net_seeds = if global::is_floonet() {
			FLOONET_DNS_SEEDS
		} else {
			MAINNET_DNS_SEEDS
		};
		for dns_seed in net_seeds {
			let temp_addresses = addresses.clone();
			debug!("Retrieving seed nodes from dns {}", dns_seed);
			match (dns_seed.to_owned(), 0).to_socket_addrs() {
				Ok(addrs) => addresses.append(
					&mut (addrs
						.map(|mut addr| {
							addr.set_port(13414);
							addr
						})
						.filter(|addr| !temp_addresses.contains(addr))
						.collect()),
				),
				Err(e) => debug!("Failed to resolve seed {:?} got error {:?}", dns_seed, e),
			}
		}
		debug!("Retrieved seed addresses: {:?}", addresses);
		addresses
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>) -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(move || {
		addrs_str
			.iter()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>()
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn preferred_peers(addrs_str: Vec<String>) -> Option<Vec<SocketAddr>> {
	if addrs_str.is_empty() {
		None
	} else {
		Some(
			addrs_str
				.iter()
				.map(|s| s.parse().unwrap())
				.collect::<Vec<_>>(),
		)
	}
}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate base64;
extern crate byteorder;
extern crate rand;
extern crate docopt;
extern crate rustc_serialize;

use std::io::{self, Write};
use std::fs::File;
use std::convert::AsRef;
use std::path::Path;
use rand::Rng;
use rand::os::OsRng;
use crypto::ed25519;
use byteorder::{BigEndian, WriteBytesExt};
use docopt::Docopt;

const KEYNUMLEN : usize = 8;
const PUBLICBYTES : usize = 32;
const SECRETBYTES : usize = 64;
const SIGBYTES : usize = 64;

const PKGALG : [u8; 2] = *b"Ed";
const KDFALG : [u8; 2] = *b"BK";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
signify-rs

Usage:
  signify -h
  signify -G [-c <comment>] -p <pubkey> -s <seckey>
  signify -V [-x <sigfile>] -p <pubkey> -m <message>

Options:
  -h --help              Show this screen.
  -c <comment>  Specify the comment to be added during key generation.
  -m <message>  When signing, the file containing the message to sign.  When verifying, the file containing the
                message to verify.  When verifying with -e, the file to create.
  -p <pubkey>   Public key produced by -G, and used by -V to check a signature.
  -s <seckey>   Secret (private) key produced by -G, and used by -S to sign a message.
  -x <sigfile>  The signature file to create or verify.  The default is <message>.sig.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_V: bool,
    flag_G: bool,

    flag_x: Option<String>,
    flag_c: Option<String>,

    flag_p: String,
    flag_s: String,
    flag_m: String,
}

struct PublicKey {
    pkgalg: [u8; 2],
    keynum: [u8; KEYNUMLEN],
    publkey: [u8; PUBLICBYTES],
}

struct PrivateKey {
   pkgalg: [u8; 2],
   kdfalg: [u8; 2],
   kdfrounds: u32,
   salt: [u8; 16],
   checksum: [u8; 8],
   keynum: [u8; KEYNUMLEN],
   seckey: [u8; SECRETBYTES],
}

struct Signature {
    pkgalg: [u8; 2],
    keynum: [u8; KEYNUMLEN],
    sig: [u8; SIGBYTES],
}

impl PublicKey {
    fn with_key_and_keynum(key: [u8; PUBLICBYTES], keynum: [u8; KEYNUMLEN]) -> PublicKey {
        PublicKey {
            pkgalg: PKGALG,
            keynum: keynum,
            publkey: key,
        }
    }

    fn write<W: Write>(&self, mut w: W) -> Result<(), io::Error> {
        try!(w.write(&self.pkgalg));
        try!(w.write(&self.keynum));
        try!(w.write(&self.publkey));

        Ok(())
    }

    fn verify(&self, msg: &[u8], signature: &Signature) -> bool {
        ed25519::verify(msg, &self.publkey, &signature.sig)
    }
}

impl PrivateKey {
    fn write<W: Write>(&self, mut w: W) -> Result<(), io::Error> {
        try!(w.write(&self.pkgalg));
        try!(w.write(&self.kdfalg));
        try!(w.write_u32::<BigEndian>(self.kdfrounds));
        try!(w.write(&self.salt));
        try!(w.write(&self.checksum));
        try!(w.write(&self.keynum));
        try!(w.write(&self.seckey));

        Ok(())
    }

    fn sign(&self, msg: &[u8]) -> Signature {
        let signature = ed25519::signature(msg, &self.seckey);
        Signature {
            pkgalg: PKGALG,
            keynum: self.keynum,
            sig: signature
        }
    }
}

impl Signature {
    fn write<W: Write>(&self, mut w: W) -> Result<(), io::Error> {
        try!(w.write(&self.pkgalg));
        try!(w.write(&self.keynum));
        try!(w.write(&self.sig));

        Ok(())
    }

    fn verify(&self, msg: &[u8], pkey: &PublicKey) -> bool {
        ed25519::verify(msg, &pkey.publkey, &self.sig)
    }
}

fn write_base64_file<P: AsRef<Path>>(file: P, comment: &str, buf: &[u8]) -> Result<(), io::Error> {
    let mut f = File::create(file).unwrap();

    try!(write!(f, "untrusted comment: "));
    try!(write!(f, "{}\n", comment));
    let out = base64::encode(buf);
    try!(write!(f, "{}\n", out));

    Ok(())
}

fn verify(pubkey_path: String, msg_path: String, signature_path: Option<String>) {
}

fn generate(pubkey_path: String, privkey_path: String, comment: Option<String>) {
    let mut keynum = [0; KEYNUMLEN];

    let mut rng = OsRng::new().expect("Can't create random number generator");
    rng.fill_bytes(&mut keynum);

    let (skey, pkey) = ed25519::keypair(&[]);
    let public_key = PublicKey::with_key_and_keynum(pkey, keynum);

    let mut out = vec![];
    public_key.write(&mut out);

    let comment = match comment {
        Some(s) => s,
        None    => "signify".into()
    };

    let comment = format!("{} public key", comment);
    write_base64_file(&pubkey_path, &comment, &out).unwrap()
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_V {
        verify(args.flag_p, args.flag_m, args.flag_x);
    } else if args.flag_G {
        generate(args.flag_p, args.flag_s, args.flag_c);
    }
}
<commit_msg>feat: Verify given signature<commit_after>extern crate crypto;
extern crate base64;
extern crate byteorder;
extern crate rand;
extern crate docopt;
extern crate rustc_serialize;

use std::process;
use std::mem;
use std::io::prelude::*;
use std::io::{self, BufReader, Cursor};
use std::fs::File;
use std::convert::AsRef;
use std::path::Path;
use rand::Rng;
use rand::os::OsRng;
use crypto::ed25519;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use docopt::Docopt;

const KEYNUMLEN : usize = 8;
const PUBLICBYTES : usize = 32;
const SECRETBYTES : usize = 64;
const SIGBYTES : usize = 64;

const PKGALG : [u8; 2] = *b"Ed";
const KDFALG : [u8; 2] = *b"BK";

const COMMENTHDR : &'static str = "untrusted comment: ";
const COMMENTHDRLEN : usize = 19;
const COMMENTMAXLEN : usize = 1024;
const VERIFYWITH : &'static str = "verify with ";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
signify-rs

Usage:
  signify -h
  signify -G [-c <comment>] -p <pubkey> -s <seckey>
  signify -V [-x <sigfile>] -p <pubkey> -m <message>

Options:
  -h --help              Show this screen.
  -c <comment>  Specify the comment to be added during key generation.
  -m <message>  When signing, the file containing the message to sign.  When verifying, the file containing the
                message to verify.  When verifying with -e, the file to create.
  -p <pubkey>   Public key produced by -G, and used by -V to check a signature.
  -s <seckey>   Secret (private) key produced by -G, and used by -S to sign a message.
  -x <sigfile>  The signature file to create or verify.  The default is <message>.sig.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_V: bool,
    flag_G: bool,

    flag_x: Option<String>,
    flag_c: Option<String>,

    flag_p: String,
    flag_s: String,
    flag_m: String,
}

enum FileContent {
    PublicKey(PublicKey),
    PrivateKey(PrivateKey),
    Signature(Signature),
}

struct PublicKey {
    pkgalg: [u8; 2],
    keynum: [u8; KEYNUMLEN],
    publkey: [u8; PUBLICBYTES],
}

struct PrivateKey {
   pkgalg: [u8; 2],
   kdfalg: [u8; 2],
   kdfrounds: u32,
   salt: [u8; 16],
   checksum: [u8; 8],
   keynum: [u8; KEYNUMLEN],
   seckey: [u8; SECRETBYTES],
}

struct Signature {
    pkgalg: [u8; 2],
    keynum: [u8; KEYNUMLEN],
    sig: [u8; SIGBYTES],
}

impl PublicKey {
    fn with_key_and_keynum(key: [u8; PUBLICBYTES], keynum: [u8; KEYNUMLEN]) -> PublicKey {
        PublicKey {
            pkgalg: PKGALG,
            keynum: keynum,
            publkey: key,
        }
    }

    fn write<W: Write>(&self, mut w: W) -> Result<(), io::Error> {
        try!(w.write(&self.pkgalg));
        try!(w.write(&self.keynum));
        try!(w.write(&self.publkey));

        Ok(())
    }

    fn from_buf(buf: &[u8]) -> Result<PublicKey, io::Error> {
        assert!(buf.len() >= mem::size_of::<Self>());

        let mut buf = Cursor::new(buf);

        let mut pkgalg = [0; 2];
        let mut keynum = [0; KEYNUMLEN];
        let mut publkey = [0; PUBLICBYTES];

        try!(buf.read(&mut pkgalg));
        try!(buf.read(&mut keynum));
        try!(buf.read(&mut publkey));

        Ok(PublicKey {
            pkgalg: pkgalg,
            keynum: keynum,
            publkey: publkey,
        })
    }

    fn verify(&self, msg: &[u8], signature: &Signature) -> bool {
        ed25519::verify(msg, &self.publkey, &signature.sig)
    }
}

impl PrivateKey {
    fn write<W: Write>(&self, mut w: W) -> Result<(), io::Error> {
        try!(w.write(&self.pkgalg));
        try!(w.write(&self.kdfalg));
        try!(w.write_u32::<BigEndian>(self.kdfrounds));
        try!(w.write(&self.salt));
        try!(w.write(&self.checksum));
        try!(w.write(&self.keynum));
        try!(w.write(&self.seckey));

        Ok(())
    }

    fn from_buf(buf: &[u8]) -> Result<PrivateKey, io::Error> {
        assert!(buf.len() >= mem::size_of::<Self>());

        let mut buf = Cursor::new(buf);

        let mut pkgalg = [0; 2];
        let mut kdfalg = [0; 2];
        let mut kdfrounds = 0;
        let mut salt = [0; 16];
        let mut checksum = [0; 8];
        let mut keynum = [0; KEYNUMLEN];
        let mut seckey = [0; SECRETBYTES];

        try!(buf.read(&mut pkgalg));
        try!(buf.read(&mut kdfalg));
        kdfrounds = try!(buf.read_u32::<BigEndian>());
        try!(buf.read(&mut salt));
        try!(buf.read(&mut checksum));
        try!(buf.read(&mut keynum));
        try!(buf.read(&mut seckey));

        Ok(PrivateKey {
            pkgalg: pkgalg,
            kdfalg: kdfalg,
            kdfrounds: kdfrounds,
            salt: salt,
            checksum: checksum,
            keynum: keynum,
            seckey: seckey,
        })
    }

    fn sign(&self, msg: &[u8]) -> Signature {
        let signature = ed25519::signature(msg, &self.seckey);
        Signature {
            pkgalg: PKGALG,
            keynum: self.keynum,
            sig: signature
        }
    }
}

impl Signature {
    fn write<W: Write>(&self, mut w: W) -> Result<(), io::Error> {
        try!(w.write(&self.pkgalg));
        try!(w.write(&self.keynum));
        try!(w.write(&self.sig));

        Ok(())
    }

    fn from_buf(buf: &[u8]) -> Result<Signature, io::Error> {
        assert!(buf.len() >= mem::size_of::<Self>());

        let mut buf = Cursor::new(buf);

        let mut pkgalg = [0; 2];
        let mut keynum = [0; KEYNUMLEN];
        let mut sig = [0; SIGBYTES];

        try!(buf.read(&mut pkgalg));
        try!(buf.read(&mut keynum));
        try!(buf.read(&mut sig));

        Ok(Signature {
            pkgalg: pkgalg,
            keynum: keynum,
            sig: sig,
        })
    }

    fn verify(&self, msg: &[u8], pkey: &PublicKey) -> bool {
        ed25519::verify(msg, &pkey.publkey, &self.sig)
    }
}

fn write_base64_file<P: AsRef<Path>>(file: P, comment: &str, buf: &[u8]) -> Result<(), io::Error> {
    let mut f = File::create(file).unwrap();

    try!(write!(f, "{}", COMMENTHDR));
    try!(write!(f, "{}\n", comment));
    let out = base64::encode(buf);
    try!(write!(f, "{}\n", out));

    Ok(())
}

fn read_base64_file<P: AsRef<Path>>(file: P) -> Result<FileContent, io::Error> {
    let file_display = format!("{}", file.as_ref().display());
    let mut f = try!(File::open(file));
    let mut reader = BufReader::new(f);

    let mut comment_line = String::new();
    let len = try!(reader.read_line(&mut comment_line));

    if len == 0 || len < COMMENTHDRLEN || !comment_line.starts_with(COMMENTHDR) {
        println!("invalid comment in {}; must start with '{}'", file_display, COMMENTHDR);
        process::exit(1);
    }

    if &comment_line[len-1..len] != "\n" {
        println!("missing new line after comment in '{}'", file_display);
        process::exit(1);
    }

    if len > COMMENTHDRLEN + COMMENTMAXLEN {
        println!("comment too long");
        process::exit(1);
    }

    let mut base64_line = String::new();
    let len = try!(reader.read_line(&mut base64_line));

    if len == 0 {
        println!("missing line in {}", file_display);
        process::exit(1);
    }

    if &base64_line[len-1..len] != "\n" {
        println!("missing new line after comment in '{}'", file_display);
        process::exit(1);
    }

    let base64_line = &base64_line[0..len-1];

    let data = match base64::decode(&base64_line) {
        Ok(data) => data,
        Err(e) => {
            println!("invalid base64 encoding in {}: {:?}", file_display, e);
            process::exit(1);
        }
    };

    if &data[0..2] != PKGALG {
        println!("unsupported file {}", file_display);
        process::exit(1);
    }

    match data.len() {
        x if x == mem::size_of::<PublicKey>() => {
            return PublicKey::from_buf(&data)
                .map(FileContent::PublicKey);
        },
        x if x == mem::size_of::<PrivateKey>() => {
            return PrivateKey::from_buf(&data)
                .map(FileContent::PrivateKey);
        },
        x if x == mem::size_of::<Signature>() => {
            return Signature::from_buf(&data)
                .map(FileContent::Signature);
        },
        x => {
            println!("unsupported file {}", file_display);
            process::exit(1);
        },
    };

    Err(io::Error::from_raw_os_error(2))
}

fn verify(pubkey_path: String, msg_path: String, signature_path: Option<String>) {
    let pkey = match read_base64_file(&pubkey_path) {
        Ok(FileContent::PublicKey(pkey)) => pkey,
        _ => {
            println!("an error occured.");
            process::exit(2);
        }
    };

    let signature_path = match signature_path {
        Some(path) => path,
        None => format!("{}.sig", msg_path)
    };

    let signature = match read_base64_file(&signature_path) {
        Ok(FileContent::Signature(sig)) => sig,
        _ => {
            println!("Can't read signature from '{}'", signature_path);
            process::exit(2);
        }
    };

    let mut msgfile = File::open(&msg_path).expect(&format!("Can't open message file '{}'", msg_path));
    let mut msg = vec![];
    msgfile.read_to_end(&mut msg).expect(&format!("Can't read file '{}'", msg_path));

    if signature.verify(&msg, &pkey) {
        println!("Signature Verified");
    } else {
        println!("signature verification failed");
        process::exit(1);
    }
}

fn generate(pubkey_path: String, privkey_path: String, comment: Option<String>) {
    let mut keynum = [0; KEYNUMLEN];

    let mut rng = OsRng::new().expect("Can't create random number generator");
    rng.fill_bytes(&mut keynum);

    let (skey, pkey) = ed25519::keypair(&[]);
    let public_key = PublicKey::with_key_and_keynum(pkey, keynum);

    let mut out = vec![];
    public_key.write(&mut out);

    let comment = match comment {
        Some(s) => s,
        None    => "signify".into()
    };

    let comment = format!("{} public key", comment);
    write_base64_file(&pubkey_path, &comment, &out).unwrap()
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_V {
        verify(args.flag_p, args.flag_m, args.flag_x);
    } else if args.flag_G {
        generate(args.flag_p, args.flag_s, args.flag_c);
    }
}
<|endoftext|>


<commit_before>#![feature(fs_walk, custom_derive, plugin, iter_arith, result_expect, test)]
#![plugin(serde_macros)]
extern crate serde;
extern crate serde_json;
extern crate nalgebra;
extern crate test;

use serde::Deserialize;
use serde_json::Value as JsonValue;
use serde_json::Deserializer;

use nalgebra::{DMat, DVec, Iterable};

use std::io::{BufReader, BufRead};
use std::fs::{File, WalkDir, read_dir, walk_dir};
use std::env::args;
use std::collections::HashSet;

#[derive(Debug, Serialize, Deserialize)]
struct Package {
    name: String,
    vers: String,
    deps: Vec<Dep>,
    cksum: String,
    features: JsonValue,
    yanked: bool,
}

#[derive(Debug, Serialize, Deserialize)]
struct Dep {
    name: String,
    req: String,
    features: Vec<String>,
    optional: bool,
    default_features: bool,
    target: Option<String>,
    kind: Option<String>,
}

fn get_packages(path: &str) -> Vec<Package> {
    read_dir(path)
        .unwrap()
        .map(Result::unwrap)
        .filter(|dir| {
            let path = dir.path();
            let file = path.file_name().unwrap().to_str().unwrap();
            file != ".git" && file != "config.json"
        })
        .flat_map::<WalkDir, _>(|dir| walk_dir(dir.path()).unwrap())
        .map(Result::unwrap)
        .filter(|file| file.file_type().unwrap().is_file())
        .map(|file| {
            let reader = BufReader::new(File::open(file.path()).unwrap());
            let latest_version = reader.lines().last().unwrap().unwrap();
            let mut deserializer = Deserializer::new(latest_version.as_bytes().iter().map(|b| Ok(*b)));
            let pkg = Package::deserialize(&mut deserializer).unwrap();
            pkg
        })
        .collect()
}

fn cargo_rank(packages: &[Package]) -> Vec<(&Package, f64)> {
    let damp = 0.85;
    let starting_rank = 1.0 / packages.len() as f64;
    let mut cargo_ranks = DVec::from_elem(packages.len(), starting_rank);
    let deps: Vec<f64> = packages.iter().flat_map(|pkg| {
        let deps: HashSet<_> = pkg.deps.iter().map(|dep| &*dep.name).collect();
        let num_deps = deps.len() as f64;
        packages.iter().map(move |dep| if deps.len() == 0 {
            1.0 / packages.len() as f64
        } else {
            if deps.contains(&*dep.name) { 1.0 / num_deps } else { 0.0 }
        })
    }).collect();
    let deps = DMat::from_col_vec(packages.len(), packages.len(), &deps);

    let mut delta = std::f64::MAX;
    let threshold = 0.000001;
    let starting_ranks = DVec::from_elem(packages.len(), (1.0 - damp) / packages.len() as f64);
    while delta > threshold {
        let new_ranks = starting_ranks.clone() + (deps.clone() * cargo_ranks.clone()) * damp;
        delta = cargo_ranks.iter().zip(new_ranks.iter()).map(|(old, new)| (old - new).abs()).sum();
        println!("Delta: {}", delta);
        cargo_ranks = new_ranks;
    }
    let mut ranks: Vec<_> = packages.iter().zip(cargo_ranks.iter().cloned()).collect();
    ranks.sort_by(|&(_, rank1), &(_, rank2)| rank1.partial_cmp(&rank2).unwrap().reverse());
    ranks
}

fn main() {
    let mut args = std::env::args().skip(1);
    let path = args.next().unwrap();
    let packages = get_packages(&path);
    let limit = args.next().unwrap().parse().expect("Not a number?");

    println!("ranks:");
    let ranks = cargo_rank(&packages);
    for (i, (pkg, rank)) in ranks.into_iter().take(limit).enumerate() {
        println!("{}. {} ({})", i + 1, pkg.name, rank);
    }
}

#[cfg(test)]
mod tests {
    use super::{get_packages, cargo_rank};
    use test::Bencher;

    #[bench]
    #[ignore]
    fn bench_cargo_rank(b: &mut Bencher) {
        let packages = get_packages("../crates.io-index/");
        b.iter(|| cargo_rank(&packages));
    }
}
<commit_msg>Remove unnecessary parens<commit_after>#![feature(fs_walk, custom_derive, plugin, iter_arith, result_expect, test)]
#![plugin(serde_macros)]
extern crate serde;
extern crate serde_json;
extern crate nalgebra;
extern crate test;

use serde::Deserialize;
use serde_json::Value as JsonValue;
use serde_json::Deserializer;

use nalgebra::{DMat, DVec, Iterable};

use std::io::{BufReader, BufRead};
use std::fs::{File, WalkDir, read_dir, walk_dir};
use std::env::args;
use std::collections::HashSet;

#[derive(Debug, Serialize, Deserialize)]
struct Package {
    name: String,
    vers: String,
    deps: Vec<Dep>,
    cksum: String,
    features: JsonValue,
    yanked: bool,
}

#[derive(Debug, Serialize, Deserialize)]
struct Dep {
    name: String,
    req: String,
    features: Vec<String>,
    optional: bool,
    default_features: bool,
    target: Option<String>,
    kind: Option<String>,
}

fn get_packages(path: &str) -> Vec<Package> {
    read_dir(path)
        .unwrap()
        .map(Result::unwrap)
        .filter(|dir| {
            let path = dir.path();
            let file = path.file_name().unwrap().to_str().unwrap();
            file != ".git" && file != "config.json"
        })
        .flat_map::<WalkDir, _>(|dir| walk_dir(dir.path()).unwrap())
        .map(Result::unwrap)
        .filter(|file| file.file_type().unwrap().is_file())
        .map(|file| {
            let reader = BufReader::new(File::open(file.path()).unwrap());
            let latest_version = reader.lines().last().unwrap().unwrap();
            let mut deserializer = Deserializer::new(latest_version.as_bytes().iter().map(|b| Ok(*b)));
            let pkg = Package::deserialize(&mut deserializer).unwrap();
            pkg
        })
        .collect()
}

fn cargo_rank(packages: &[Package]) -> Vec<(&Package, f64)> {
    let damp = 0.85;
    let starting_rank = 1.0 / packages.len() as f64;
    let mut cargo_ranks = DVec::from_elem(packages.len(), starting_rank);
    let deps: Vec<f64> = packages.iter().flat_map(|pkg| {
        let deps: HashSet<_> = pkg.deps.iter().map(|dep| &*dep.name).collect();
        let num_deps = deps.len() as f64;
        packages.iter().map(move |dep| if deps.len() == 0 {
            1.0 / packages.len() as f64
        } else {
            if deps.contains(&*dep.name) { 1.0 / num_deps } else { 0.0 }
        })
    }).collect();
    let deps = DMat::from_col_vec(packages.len(), packages.len(), &deps);

    let mut delta = std::f64::MAX;
    let threshold = 0.000001;
    let starting_ranks = DVec::from_elem(packages.len(), (1.0 - damp) / packages.len() as f64);
    while delta > threshold {
        let new_ranks = starting_ranks.clone() + deps.clone() * cargo_ranks.clone() * damp;
        delta = cargo_ranks.iter().zip(new_ranks.iter()).map(|(old, new)| (old - new).abs()).sum();
        println!("Delta: {}", delta);
        cargo_ranks = new_ranks;
    }
    let mut ranks: Vec<_> = packages.iter().zip(cargo_ranks.iter().cloned()).collect();
    ranks.sort_by(|&(_, rank1), &(_, rank2)| rank1.partial_cmp(&rank2).unwrap().reverse());
    ranks
}

fn main() {
    let mut args = std::env::args().skip(1);
    let path = args.next().unwrap();
    let packages = get_packages(&path);
    let limit = args.next().unwrap().parse().expect("Not a number?");

    println!("ranks:");
    let ranks = cargo_rank(&packages);
    for (i, (pkg, rank)) in ranks.into_iter().take(limit).enumerate() {
        println!("{}. {} ({})", i + 1, pkg.name, rank);
    }
}

#[cfg(test)]
mod tests {
    use super::{get_packages, cargo_rank};
    use test::Bencher;

    #[bench]
    #[ignore]
    fn bench_cargo_rank(b: &mut Bencher) {
        let packages = get_packages("../crates.io-index/");
        b.iter(|| cargo_rank(&packages));
    }
}
<|endoftext|>