1
use cargo_scout_lib::config::rust::CargoConfig;
2
use cargo_scout_lib::linter::clippy::Clippy;
3
use cargo_scout_lib::linter::rustfmt::RustFmt;
4
use cargo_scout_lib::linter::Lint;
5
use cargo_scout_lib::scout::Scout;
6
use cargo_scout_lib::vcs::git::Git;
7
use cargo_scout_lib::Error;
8
use structopt::StructOpt;
9

10
#[derive(StructOpt)]
11
#[structopt(
12
    name = "cargo-scout",
13
    author,
14
    about = "Leave the codebase better than when you found it."
15
)]
16
enum Command {
17
    Fmt(FmtOptions),
18
    Lint(LintOptions),
19
}
20

21
#[derive(Debug, StructOpt)]
22
struct FmtOptions {
23
    #[structopt(
24
        short = "b",
25
        long = "branch",
26
        value_name = "branch",
27
        default_value = "HEAD"
28
    )]
29
    /// Set the target branch
30
    branch: String,
31
    #[structopt(short = "t", long = "cargo-toml", default_value = "./Cargo.toml")]
32
    /// Pass the path of the `Cargo.toml` file
33
    cargo_toml: String,
34
    #[structopt(short = "w", long = "without-error")]
35
    /// Set to display the warnings without actually returning an error
36
    without_error: bool,
37
}
38

39
#[derive(Debug, StructOpt)]
40
#[allow(clippy::struct_excessive_bools)]
41
struct LintOptions {
42
    #[structopt(short = "v", long = "verbose")]
43
    /// Set the verbosity level
44
    verbose: bool,
45
    #[structopt(long = "no-default-features")]
46
    /// Pass the no default features flag to clippy
47
    no_default_features: bool,
48
    #[structopt(long = "all-features")]
49
    /// Pass the all features flag to clippy
50
    all_features: bool,
51
    #[structopt(long = "features")]
52
    /// Pass features to clippy
53
    features: Option<String>,
54
    #[structopt(
55
        short = "b",
56
        long = "branch",
57
        value_name = "branch",
58
        default_value = "HEAD"
59
    )]
60
    /// Set the target branch
61
    branch: String,
62

63
    #[structopt(short = "t", long = "cargo-toml", default_value = "./Cargo.toml")]
64
    /// Pass the path of the `Cargo.toml` file
65
    cargo_toml: String,
66
    #[structopt(short = "w", long = "without-error")]
67
    /// Set to display the warnings without actually returning an error
68
    without_error: bool,
69
    #[structopt(short = "p", long = "preview")]
70
    /// Enable nightly features (e.g. get lints even after the build has already been done.)
71
    preview: bool,
72
}
73

74
// There is no logic to test
75
#[cfg(not(tarpaulin_include))]
76
fn main() -> Result<(), Error> {
77
    match Command::from_args() {
78
        Command::Fmt(opts) => run_fmt(opts),
79
        Command::Lint(opts) => run_lint(opts),
80
    }
81
}
82

83
#[cfg(not(tarpaulin_include))]
84
fn run_lint(opts: LintOptions) -> Result<(), Error> {
85
    let fail_if_errors = opts.without_error;
86

87
    let vcs = Git::with_target(opts.branch);
88
    let config = CargoConfig::from_manifest_path(opts.cargo_toml)?;
89
    let mut linter = Clippy::default();
90
    linter
91
        .set_verbose(opts.verbose)
92
        .set_no_default_features(opts.no_default_features)
93
        .set_all_features(opts.all_features)
94
        .set_features(opts.features)
95
        .set_preview(opts.preview);
96
    let scout = Scout::new(vcs, config, linter);
97
    let relevant_lints = scout.run()?;
98
    return_warnings(&relevant_lints, fail_if_errors)
99
}
100

101
#[cfg(not(tarpaulin_include))]
102
fn run_fmt(opts: FmtOptions) -> Result<(), Error> {
103
    let fail_if_errors = opts.without_error;
104

105
    let vcs = Git::with_target(opts.branch);
106
    let config = CargoConfig::from_manifest_path(opts.cargo_toml)?;
107
    let linter = RustFmt::default();
108

109
    let scout = Scout::new(vcs, config, linter);
110
    let relevant_lints = scout.run()?;
111
    return_warnings(&relevant_lints, fail_if_errors)
112
}
113

114 1
fn return_warnings(lints: &[Lint], without_error: bool) -> Result<(), Error> {
115 1
    if lints.is_empty() {
116 1
        println!("No issues in your diff, you're good to go!");
117 1
        Ok(())
118
    } else {
119 1
        display_warnings(&lints);
120 1
        if without_error {
121 1
            Ok(())
122
        } else {
123 1
            Err(Error::NotClean)
124
        }
125
    }
126
}
127

128 1
fn display_warnings(warnings: &[Lint]) {
129 1
    for w in warnings {
130 1
        for l in w.message.split('\n') {
131 1
            println!("{}", l);
132
        }
133
    }
134 1
    if warnings.len() == 1 {
135 1
        println!("Cargo scout found a warning");
136
    } else {
137 0
        println!("Cargo scout found {} warnings", warnings.len());
138
    }
139
}
140

141
#[cfg(test)]
142
mod tests {
143
    use super::{return_warnings, Lint};
144
    use cargo_scout_lib::linter::Location;
145
    #[test]
146 1
    fn test_return_status_with_lints() {
147 1
        let lints = vec![Lint {
148 1
            message: String::new(),
149 1
            location: Location {
150 1
                path: String::new(),
151 1
                lines: [0, 0],
152
            },
153
        }];
154

155 1
        assert!(return_warnings(&lints, true).is_ok());
156 1
        assert!(return_warnings(&lints, false).is_err());
157
    }
158

159
    #[test]
160 1
    fn test_return_status_without_existing_lints() {
161 1
        let lints: Vec<Lint> = Vec::new();
162

163 1
        assert!(return_warnings(&lints, true).is_ok());
164 1
        assert!(return_warnings(&lints, false).is_ok());
165
    }
166
}

Read our documentation on viewing source code .

Loading