cargo_testify/
lib.rs

1extern crate clap;
2extern crate glob;
3extern crate notify;
4extern crate notify_rust;
5extern crate regex;
6#[macro_use]
7extern crate error_chain;
8
9use clap::{App, Arg, SubCommand};
10
11mod config;
12mod errors;
13mod reactor;
14mod report;
15mod report_builder;
16use config::ConfigBuilder;
17use reactor::Reactor;
18
19pub fn run() {
20    let matches = App::new("cargo")
21        .bin_name("cargo")
22        .help_message("")
23        .version_message("")
24        .subcommand(
25            SubCommand::with_name("testify")
26            .version("0.2.0")
27            .author("Sergey Potapov <blake131313@gmail.com>")
28            .about("Automatically runs tests for Rust project and notifies about the result.\nSource code: https://github.com/greyblake/cargo-testify")
29            .arg(Arg::with_name("includes")
30                 .short("i")
31                 .long("include")
32                 .takes_value(true)
33                 .help("Comma separated list of include pattern in addition to the predefined default patterns"))
34            .arg(Arg::with_name("cargo_test_args")
35                 .multiple(true)
36                 .last(true))
37        )
38        .get_matches();
39
40    let cargo_test_args = if let Some(matches) = matches.subcommand_matches("testify") {
41        matches
42            .values_of("cargo_test_args")
43            .map(|vals| vals.collect::<Vec<_>>())
44            .unwrap_or(vec![])
45    } else {
46        vec![]
47    };
48
49    let include_patterns = matches
50        .subcommand_matches("testify")
51        .and_then(|m| m.value_of("includes"))
52        .map(|vals| vals.split(',').collect::<Vec<_>>())
53        .unwrap_or(vec![]);
54
55    let project_dir = detect_project_dir();
56    let config = ConfigBuilder::new()
57        .project_dir(project_dir)
58        .include_patterns(&include_patterns)
59        .cargo_test_args(cargo_test_args)
60        .build()
61        .unwrap();
62
63    Reactor::new(config).start()
64}
65
66/// Search for Cargo.toml file starting from the current directory,
67/// going with every step to parent directory. If directory with
68/// Cargo.toml is found return it, otherwise print error message and
69/// terminate the process.
70fn detect_project_dir() -> std::path::PathBuf {
71    let current_dir = std::env::current_dir().expect("Failed to get current directory");
72    let mut optional_dir = Some(current_dir.as_path());
73
74    while let Some(dir) = optional_dir {
75        let cargo_toml = dir.join("Cargo.toml");
76        if cargo_toml.is_file() {
77            return dir.to_path_buf();
78        }
79        optional_dir = dir.parent();
80    }
81
82    eprintln!(
83        "Error: could not find `Cargo.toml` in {:?} or any parent directory.",
84        current_dir
85    );
86    std::process::exit(1);
87}