1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
use std::path::PathBuf;
use clap::{Args, Parser};
use glob::Pattern;
#[cfg(feature = "coveralls")]
use crate::config::Ci;
use crate::config::{Color, Mode, OutputFile, RunType, TraceEngine};
#[derive(Debug, Parser)]
#[command(name = "cargo-tarpaulin")]
#[command(bin_name = "cargo")]
#[command(author, version, about, long_about = None)]
pub enum CargoTarpaulinCli {
Tarpaulin(TarpaulinCli),
}
impl CargoTarpaulinCli {
pub fn from_args() -> TarpaulinCli {
match TarpaulinCli::try_parse() {
Ok(tarpaulin_cli) => tarpaulin_cli,
Err(err) if !err.use_stderr() => err.exit(),
Err(_) => match CargoTarpaulinCli::parse() {
CargoTarpaulinCli::Tarpaulin(tarpaulin_cli) => tarpaulin_cli,
},
}
}
}
#[derive(Debug, Parser)]
#[command(name = "tarpaulin")]
#[command(author, version, about, long_about = None)]
pub struct TarpaulinCli {
#[clap(flatten)]
pub print_flags: PrintFlagsArgs,
#[clap(flatten)]
pub config: ConfigArgs,
}
#[derive(Debug, Clone, Args)]
pub struct ConfigArgs {
#[clap(flatten)]
pub logging: LoggingArgs,
#[clap(flatten)]
pub run_types: RunTypesArgs,
/// Path to a toml file specifying a list of options this will override any other options set
#[arg(long, value_name = "FILE")]
pub config: Option<PathBuf>,
/// Ignore any project config files
#[arg(long)]
pub ignore_config: bool,
/// Test only the specified binary
#[arg(long, value_name = "NAME", num_args = 0..)]
pub bin: Vec<String>,
/// Test only the specified example
#[arg(long, value_name = "NAME", num_args = 0..)]
pub example: Vec<String>,
/// Test only the specified test target
#[arg(long, value_name = "NAME", num_args = 0..)]
pub test: Vec<String>,
/// Test only the specified bench target
#[arg(long, value_name = "NAME", num_args = 0..)]
pub bench: Vec<String>,
/// Run all tests regardless of failure
#[arg(long)]
pub no_fail_fast: bool,
/// Build artefacts with the specified profile
#[arg(long, value_name = "NAME")]
pub profile: Option<String>,
/// Ignore lines of test functions when collecting coverage (default)
#[arg(long)]
pub ignore_tests: bool,
/// Stops tarpaulin from building projects with -Clink-dead-code
#[arg(long)]
pub no_dead_code: bool,
/// Include lines of test functions when collecting coverage
#[arg(long)]
pub include_tests: bool,
/// Ignore panic macros in tests
#[arg(long)]
pub ignore_panics: bool,
/// Counts the number of hits during coverage
#[arg(long)]
pub count: bool,
/// Run ignored tests as well
#[arg(long, short)]
pub ignored: bool,
/// Line coverage
#[arg(long, short)]
pub line: bool,
/// The opposite of --force-clean
#[arg(long)]
pub skip_clean: bool,
/// Adds a clean stage to work around cargo bugs that may affect coverage results
#[arg(long)]
pub force_clean: bool,
/// Sets a percentage threshold for failure ranging from 0-100, if coverage is below exit with a non-zero code
#[arg(long, value_name = "PERCENTAGE")]
pub fail_under: Option<f64>,
/// Branch coverage: NOT IMPLEMENTED
#[arg(long, short)]
pub branch: bool,
/// Forwards unexpected signals to test. This is now the default behaviour
#[arg(long, short)]
pub forward: bool,
/// Coveralls key, either the repo token, or if you're using travis use $TRAVIS_JOB_ID and specify travis-{ci|pro} in --ciserver
#[arg(long, value_name = "KEY")]
pub coveralls: Option<String>,
/// URI to send report to, only used if the option --coveralls is used
#[arg(long, value_name = "URI")]
pub report_uri: Option<String>,
/// Do not include default features
#[arg(long)]
pub no_default_features: bool,
/// Features to be included in the target project
#[arg(long, value_name = "FEATURES", num_args = 0..)]
pub features: Vec<String>,
/// Build all available features
#[arg(long)]
pub all_features: bool,
/// Alias for --workspace (deprecated)
#[arg(long)]
pub all: bool,
/// Test all packages in the workspace
#[arg(long)]
pub workspace: bool,
/// Package id specifications for which package should be build. See cargo help pkgid for more info
#[arg(long, short, alias = "package", value_name = "PACKAGE", num_args = 0..)]
pub packages: Vec<String>,
/// Package id specifications to exclude from coverage. See cargo help pkgid for more info
#[arg(long, short, value_name = "PACKAGE", num_args = 0..)]
pub exclude: Vec<String>,
/// Exclude given files from coverage results has * wildcard
#[arg(long, value_name = "FILE", num_args = 0..)]
pub exclude_files: Vec<Pattern>,
/// Integer for the maximum time in seconds without response from test before timeout (default is 1 minute).
#[arg(long, short, value_name = "SECONDS")]
pub timeout: Option<u64>,
/// Delay after test to collect coverage profiles
#[arg(long, value_name = "SECONDS")]
pub post_test_delay: Option<u64>,
/// Follow executed processes capturing coverage information if they're part of your project.
#[arg(long)]
pub follow_exec: bool,
/// Build in release mode.
#[arg(long)]
pub release: bool,
/// Compile tests but don't run coverage
#[arg(long)]
pub no_run: bool,
/// 'Don't supply an explicit `--test-threads` argument to test executable. By default tarpaulin will infer the default rustc would pick if not ran via tarpaulin and set it
#[arg(long)]
pub implicit_test_threads: bool,
/// Do not update Cargo.lock
#[arg(long)]
pub locked: bool,
/// Do not update Cargo.lock or any caches
#[arg(long)]
pub frozen: bool,
/// Compilation target triple
#[arg(long, value_name = "TRIPLE")]
pub target: Option<String>,
/// Directory for all generated artifacts
#[arg(long, value_name = "DIR")]
pub target_dir: Option<PathBuf>,
/// Run without accessing the network
#[arg(long)]
pub offline: bool,
/// Remove --cfg=tarpaulin from the RUSTFLAG
#[arg(long)]
pub avoid_cfg_tarpaulin: bool,
/// Number of parallel jobs, defaults to # of CPUs
#[arg(long, short, value_name = "N")]
pub jobs: Option<usize>,
/// Rustflags to add when building project (can also be set via RUSTFLAGS env var)
#[arg(long, value_name = "FLAGS")]
pub rustflags: Option<String>,
/// Other object files to load which contain information for llvm coverage - must have been compiled with llvm coverage instrumentation (ignored for ptrace)
#[arg(long, value_name = "objects", num_args = 0..)]
pub objects: Vec<PathBuf>,
/// List of unstable nightly only flags
#[arg(short = 'Z', value_name = "FEATURES", num_args = 0..)]
pub unstable_features: Vec<String>,
/// Output format of coverage report
#[arg(long, short, value_enum, value_name = "FMT", num_args = 0.., ignore_case = true)]
pub out: Vec<OutputFile>,
/// Coverage tracing backend to use
#[arg(long, value_enum, value_name = "ENGINE", ignore_case = true)]
pub engine: Option<TraceEngine>,
/// Specify a custom directory to write report files
#[arg(long, value_name = "PATH")]
pub output_dir: Option<PathBuf>,
/// cargo subcommand to run. So far only test and build are supported
#[arg(long, value_enum, value_name = "CMD", ignore_case = true)]
pub command: Option<Mode>,
/// Calculates relative paths to root directory. If --manifest-path isn't specified it will look for a Cargo.toml in root
#[arg(long, short, value_name = "DIR")]
pub root: Option<PathBuf>,
/// Path to Cargo.toml
#[arg(long, value_name = "PATH")]
pub manifest_path: Option<PathBuf>,
#[cfg(feature = "coveralls")]
/// CI server being used, if unspecified tarpaulin may automatically infer for coveralls uploads
#[arg(long, value_name = "SERVICE")]
pub ciserver: Option<Ci>,
/// Option to fail immediately after a single test fails
#[arg(long)]
pub fail_immediately: bool,
/// Arguments to be passed to the test executables can be used to filter or skip certain tests
#[arg(last = true)]
pub args: Vec<String>,
}
#[derive(Debug, Clone, Copy, Args)]
pub struct LoggingArgs {
/// Coloring: auto, always, never
#[arg(long, value_enum, value_name = "WHEN", ignore_case = true)]
pub color: Option<Color>,
/// Show debug output - this is used for diagnosing issues with tarpaulin
#[arg(long)]
pub debug: bool,
/// Show extra output
#[arg(long, short)]
pub verbose: bool,
/// Log tracing events and save to a json file. Also, enabled when --debug is used
#[arg(long)]
pub dump_traces: bool,
}
#[derive(Debug, Clone, Copy, Args)]
pub struct PrintFlagsArgs {
/// Print the RUSTFLAGS options that tarpaulin will compile your program with and exit
#[arg(long)]
pub print_rust_flags: bool,
/// Print the RUSTDOCFLAGS options that tarpaulin will compile any doctests with and exit
#[arg(long)]
pub print_rustdoc_flags: bool,
}
#[derive(Debug, Clone, Args)]
pub struct RunTypesArgs {
/// Type of the coverage run
#[arg(long, value_enum, value_name = "TYPE", ignore_case = true)]
pub run_types: Vec<RunType>,
/// Test all benches
#[arg(long)]
pub benches: bool,
/// Test only this library's documentation
#[arg(long)]
pub doc: bool,
/// Test all targets (excluding doctests)
#[arg(long)]
pub all_targets: bool,
/// Test only this package's library unit tests
#[arg(long)]
pub lib: bool,
/// Test all binaries
#[arg(long)]
pub bins: bool,
/// Test all examples
#[arg(long)]
pub examples: bool,
/// Test all tests
#[arg(long)]
pub tests: bool,
}
impl RunTypesArgs {
pub fn collect(self) -> Vec<RunType> {
let mut run_types = self.run_types;
if self.lib && !run_types.contains(&RunType::Lib) {
run_types.push(RunType::Lib);
}
if self.all_targets && !run_types.contains(&RunType::AllTargets) {
run_types.push(RunType::AllTargets);
}
if self.benches && !run_types.contains(&RunType::Benchmarks) {
run_types.push(RunType::Benchmarks);
}
if self.bins && !run_types.contains(&RunType::Bins) {
run_types.push(RunType::Bins);
}
if self.examples && !run_types.contains(&RunType::Examples) {
run_types.push(RunType::Examples);
}
if self.doc && !run_types.contains(&RunType::Doctests) {
run_types.push(RunType::Doctests);
}
if self.tests && !run_types.contains(&RunType::Tests) {
run_types.push(RunType::Tests);
}
run_types
}
}
#[cfg(test)]
mod tests {
use clap::CommandFactory;
use super::CargoTarpaulinCli;
#[test]
fn verify_args() {
CargoTarpaulinCli::command().debug_assert()
}
#[test]
#[ignore = "Manual use only"]
fn show_help() {
let help = CargoTarpaulinCli::command().render_help();
println!("{help}");
}
}