ci-cargo

CI for Rust code.
git clone https://git.philomathiclife.com/repos/ci-cargo
Log | Files | Refs | README

args.rs (125243B)


      1 use super::{
      2     cargo::{CargoErr, Check, Clippy, Options, Test, Toolchain},
      3     manifest::PowerSet,
      4 };
      5 use core::{
      6     fmt::{self, Display, Formatter},
      7     ops::IndexMut as _,
      8 };
      9 use std::{
     10     ffi::OsString,
     11     io::{self, Error, StderrLock, StdoutLock, Write as _},
     12     path::PathBuf,
     13     time::Instant,
     14 };
     15 /// Help message.
     16 pub(crate) const HELP_MSG: &str = "Continuous integration of all features using cargo
     17 
     18 Usage: ci-cargo [COMMAND] [OPTIONS] [COMMAND] [OPTIONS] [COMMAND] [OPTIONS]
     19 
     20 Commands:
     21     check      cargo check
     22     clippy     cargo clippy
     23     help       This message
     24     test       cargo test
     25     version    Prints version info
     26 
     27 Global Options:
     28   --allow-implied features     Allow implied features from optional dependencies
     29   --cargo-home <PATH>          Set the storage directory used by cargo
     30   --cargo-path <PATH>          Set the path cargo is in. Defaults to cargo
     31   --color                      --color always is passed to each command; otherwise --color never is
     32   --default-toolchain          cargo is invoked as is (i.e., cargo +stable is never used)
     33   --dir <PATH>                 Set the working directory
     34   --ignore-compile-errors      compile_error!s are ignored
     35   --ignore-features <feats>    Ignore the provided comma-separated features
     36   --ignore-msrv                --ignore-rust-version is passed to each command for the default toolchain
     37   --progress                   Writes the progress to stdout
     38   --rustup-home <PATH>         Set the storage directory used by rustup
     39   --skip-msrv                  cargo +<MSRV> is not used
     40   --summary                    Writes the toolchain(s) and combinations of features used to stdout on success
     41 
     42 Check Options:
     43   --all-targets    --all-targets is passed
     44   --benches        --benches is passed
     45   --bins           --bins is passed
     46   --examples       --examples is passed
     47   --lib            --lib is passed
     48   --tests          --tests is passed
     49 
     50 Clippy Options:
     51   --all-targets      --all-targets is passed
     52   --benches          --benches is passed
     53   --bins             --bins is passed
     54   --deny-warnings    -- -Dwarnings is passed
     55   --examples         --examples is passed
     56   --lib              --lib is passed
     57   --tests            --tests is passed
     58 
     59 Test Options:
     60   --all-targets        cargo test --all-targets and cargo test --doc are run
     61   --benches            --benches is passed
     62   --bins               --bins is passed
     63   --doc                --doc is passed
     64   --examples           --examples is passed
     65   --ignored            -- --ignored is passed
     66   --include-ignored    -- --include-ignored is passed
     67   --lib                --lib is passed
     68   --tests              --tests is passed
     69 
     70 The following conditions must be met:
     71 
     72 * the help and version commands mustn't be combined with other commands or options
     73 * any unique combination of check, clippy, and test can be used
     74 * command-specific options must be unique for a given command
     75 * global options are allowed after any command but must be unique
     76 * command-specific options must follow the command
     77 * the test options --ignored and --include-ignored are mutually exclusive
     78 * --all-targets mustn't be combined with other targets (e.g., --lib)
     79 * the test option --doc mustn't be combined with other targets
     80 * --ignore-msrv is not allowed if the stable toolchain is used
     81 
     82 cargo +stable will be used to run the command(s) if all of the following conditions are met:
     83 
     84 * --default-toolchain was not passed
     85 * rust-toolchain.toml does not exist in the package directory nor its ancestor directories
     86 * --rustup-home was not passed for platforms that don't support rustup
     87 
     88 If the above are not met, cargo will be used instead. cargo +<MSRV> will also be used
     89 if all of the following conditions are met:
     90 
     91 * --skip-msrv was not passed
     92 * package has an MSRV defined via rust-version that is semantically less than the stable or not
     93   equivalent to the default toolchain used
     94 * --rustup-home was passed or the platform supports rustup
     95 
     96 For the toolchain(s) used, the command(s) are run for each combination of features sans any provided
     97 with --ignore-features. Features provided with --ignore-features must be unique and represent valid
     98 features in the package. An empty value is interpreted as the empty set of features.
     99 ";
    100 /// `"help"`.
    101 const HELP: &str = "help";
    102 /// `"version"`.
    103 const VERSION: &str = "version";
    104 /// `"check"`.
    105 const CHECK: &str = "check";
    106 /// `"clippy"`.
    107 const CLIPPY: &str = "clippy";
    108 /// `"test"`.
    109 const TEST: &str = "test";
    110 /// `"--all-targets"`.
    111 const ALL_TARGETS: &str = "--all-targets";
    112 /// `"--allow-implied-features"`.
    113 const ALLOW_IMPLIED_FEATURES: &str = "--allow-implied-features";
    114 /// `"--benches"`.
    115 const BENCHES: &str = "--benches";
    116 /// `"--bins"`.
    117 const BINS: &str = "--bins";
    118 /// `"--cargo-home"`.
    119 const CARGO_HOME: &str = "--cargo-home";
    120 /// `"--cargo-path"`.
    121 const CARGO_PATH: &str = "--cargo-path";
    122 /// `"--color"`.
    123 const COLOR: &str = "--color";
    124 /// `"--default-toolchain"`.
    125 const DEFAULT_TOOLCHAIN: &str = "--default-toolchain";
    126 /// `"--deny-warnings"`.
    127 const DENY_WARNINGS: &str = "--deny-warnings";
    128 /// `"--dir"`.
    129 const DIR: &str = "--dir";
    130 /// `"--doc"`.
    131 const DOC: &str = "--doc";
    132 /// `"--examples"`.
    133 const EXAMPLES: &str = "--examples";
    134 /// `"--ignore-compile-errors"`.
    135 const IGNORE_COMPILE_ERRORS: &str = "--ignore-compile-errors";
    136 /// `"--ignore-features"`.
    137 const IGNORE_FEATURES: &str = "--ignore-features";
    138 /// `"--ignore-msrv"`.
    139 const IGNORE_MSRV: &str = "--ignore-msrv";
    140 /// `"--ignored"`.
    141 const IGNORED: &str = "--ignored";
    142 /// `"--include-ignored"`.
    143 const INCLUDE_IGNORED: &str = "--include-ignored";
    144 /// `"--lib"`.
    145 const LIB: &str = "--lib";
    146 /// `"--progress"`.
    147 const PROGRESS: &str = "--progress";
    148 /// `"--rustup-home"`.
    149 const RUSTUP_HOME: &str = "--rustup-home";
    150 /// `"--skip-msrv"`.
    151 const SKIP_MSRV: &str = "--skip-msrv";
    152 /// `"--summary"`.
    153 const SUMMARY: &str = "--summary";
    154 /// `"--tests"`.
    155 const TESTS: &str = "--tests";
    156 /// `"cargo"`.
    157 const CARGO: &str = "cargo";
    158 /// `"test --doc"`.
    159 const TEST_DOC: &str = "test --doc";
    160 /// `"test --all-targets"`.
    161 const TEST_ALL_TARGETS: &str = "test --all-targets";
    162 /// `"1"`.
    163 const ONE: &str = "1";
    164 /// `"2"`.
    165 const TWO: &str = "2";
    166 /// `"3"`.
    167 const THREE: &str = "3";
    168 /// `"4"`.
    169 const FOUR: &str = "4";
    170 /// `"cargo "`.
    171 const CARGO_SPACE: &str = "cargo ";
    172 /// Error returned when parsing arguments passed to the application.
    173 #[cfg_attr(test, derive(Debug, PartialEq))]
    174 pub(crate) enum ArgsErr {
    175     /// Error when no arguments exist.
    176     NoArgs,
    177     /// Error when no command was passed.
    178     NoCommand,
    179     /// Error when an unknown argument is passed. The contained [`OsString`] is the value of the unknown command
    180     /// or option.
    181     UnknownArg(OsString),
    182     /// Error when an option is passed more than once. The contained [`OsString`] is the duplicate argument.
    183     DuplicateOption(OsString),
    184     /// Error when `help` is passed followed by a non-empty sequence of arguments.
    185     HelpWithArgs,
    186     /// Error when `version` is passed followed by a non-empty sequence of arguments.
    187     VersionWithArgs,
    188     /// Error when `--dir` is passed with no file path to the directory `ci-cargo` should run in.
    189     MissingDirPath,
    190     /// Error when `--cargo-path` is passed with no file path to the directory `cargo` should be located in.
    191     MissingCargoPath,
    192     /// Error when `--cargo-home` is passed with no file path to the storage directory `cargo` uses.
    193     MissingCargoHome,
    194     /// Error when `--rustup-home` is passed with no file path to the storage directory `rustup` uses.
    195     MissingRustupHome,
    196     /// Error when `--all-targets` is passed with other targets.
    197     AllTargets,
    198     /// Error when `--doc` is passed with other targets with the `test` command.
    199     Doc,
    200     /// Error when `--ignored` and `--include-ignored` are passed with the `test` command.
    201     IgnoredIncludeIgnored,
    202     /// Error when `--ignore-features` was not passed any features.
    203     ///
    204     /// Note to _only_ pass in the empty set in an interactive terminal,
    205     /// you likely will need to use quotes.
    206     MissingIgnoredFeatures,
    207     /// Error when `--ignore-features` is passed duplicate features.
    208     DuplicateIgnoredFeatures(OsString),
    209 }
    210 impl ArgsErr {
    211     /// Writes `self` to `stderr`.
    212     pub(crate) fn write(self, mut stderr: StderrLock<'_>) -> Result<(), Error> {
    213         const FINAL_SENTENCE: &str = " See ci-cargo help for more information.";
    214         match self {
    215             Self::NoArgs => writeln!(
    216                 stderr,
    217                 "No arguments exist including the name of the process itself.{FINAL_SENTENCE}"
    218             ),
    219             Self::NoCommand => writeln!(
    220                 stderr,
    221                 "A command was not passed as the first argument.{FINAL_SENTENCE}"
    222             ),
    223             Self::UnknownArg(arg) => {
    224                 writeln!(
    225                     stderr,
    226                     "{} is an unknown argument.{FINAL_SENTENCE}",
    227                     arg.display()
    228                 )
    229             }
    230             Self::DuplicateOption(arg) => {
    231                 writeln!(
    232                     stderr,
    233                     "{} was passed more than once.{FINAL_SENTENCE}",
    234                     arg.display()
    235                 )
    236             }
    237             Self::HelpWithArgs => {
    238                 writeln!(
    239                     stderr,
    240                     "{HELP} was passed with one or more arguments.{FINAL_SENTENCE}",
    241                 )
    242             }
    243             Self::VersionWithArgs => {
    244                 writeln!(
    245                     stderr,
    246                     "{VERSION} was passed with one or more arguments.{FINAL_SENTENCE}",
    247                 )
    248             }
    249             Self::MissingDirPath => {
    250                 writeln!(
    251                     stderr,
    252                     "{DIR} was passed without a path to the directory ci-cargo should run in.{FINAL_SENTENCE}"
    253                 )
    254             }
    255             Self::MissingCargoPath => {
    256                 writeln!(
    257                     stderr,
    258                     "{CARGO_PATH} was passed without a path to the directory cargo is located in.{FINAL_SENTENCE}"
    259                 )
    260             }
    261             Self::MissingCargoHome => {
    262                 writeln!(
    263                     stderr,
    264                     "{CARGO_HOME} was passed without a path to the cargo storage directory.{FINAL_SENTENCE}"
    265                 )
    266             }
    267             Self::MissingRustupHome => {
    268                 writeln!(
    269                     stderr,
    270                     "{RUSTUP_HOME} was passed without a path to the rustup storage directory.{FINAL_SENTENCE}"
    271                 )
    272             }
    273             Self::AllTargets => {
    274                 writeln!(
    275                     stderr,
    276                     "{ALL_TARGETS} was passed with other targets.{FINAL_SENTENCE}"
    277                 )
    278             }
    279             Self::Doc => {
    280                 writeln!(
    281                     stderr,
    282                     "{DOC} was passed with other targets.{FINAL_SENTENCE}"
    283                 )
    284             }
    285             Self::IgnoredIncludeIgnored => {
    286                 writeln!(
    287                     stderr,
    288                     "{IGNORED} and {INCLUDE_IGNORED} were both passed.{FINAL_SENTENCE}"
    289                 )
    290             }
    291             Self::MissingIgnoredFeatures => {
    292                 writeln!(
    293                     stderr,
    294                     "{IGNORE_FEATURES} was passed without any features to ignore.{FINAL_SENTENCE}"
    295                 )
    296             }
    297             Self::DuplicateIgnoredFeatures(feats) => {
    298                 writeln!(
    299                     stderr,
    300                     "{IGNORE_FEATURES} was passed {} which contains at least one duplicate feature.{FINAL_SENTENCE}",
    301                     feats.display()
    302                 )
    303             }
    304         }
    305     }
    306 }
    307 /// Options to use for `cargo`.
    308 #[expect(
    309     clippy::struct_excessive_bools,
    310     reason = "not a problem. arguable false positive based on its use"
    311 )]
    312 #[cfg_attr(test, derive(Debug, PartialEq))]
    313 pub(crate) struct Opts {
    314     /// The directory to run `ci-cargo` in.
    315     pub exec_dir: Option<PathBuf>,
    316     /// Storage directory for `rustup`.
    317     pub rustup_home: Option<PathBuf>,
    318     /// Path to `cargo`.
    319     pub cargo_path: PathBuf,
    320     /// Storage directory for `cargo`.
    321     pub cargo_home: Option<PathBuf>,
    322     /// `true` iff color should be outputted.
    323     pub color: bool,
    324     /// `true` iff `cargo` should be used instead of `cargo +stable`.
    325     pub default_toolchain: bool,
    326     /// `true` iff implied features should be allowed an tested.
    327     pub allow_implied_features: bool,
    328     /// `true` iff `compile_error`s should be ignored.
    329     pub ignore_compile_errors: bool,
    330     /// `true` iff `--ignore-rust-version` should be passed.
    331     pub ignore_msrv: bool,
    332     /// `true` iff progress should be written to `stdout`.
    333     pub progress: bool,
    334     /// `true` iff the MSRV toolchain should not be used.
    335     pub skip_msrv: bool,
    336     /// `true` iff the toolchains used and combinations of features run on should be written
    337     /// to `stdout` upon success.
    338     pub summary: bool,
    339     /// The features to ignore.
    340     ///
    341     /// Note this is empty iff there are no features to ignore. The contained features
    342     /// are distinct. The empty `String` corresponds to the empty set of features to
    343     /// ignore (i.e., --no-default-features).
    344     pub ignore_features: Vec<String>,
    345 }
    346 impl Default for Opts {
    347     fn default() -> Self {
    348         Self {
    349             exec_dir: None,
    350             rustup_home: None,
    351             cargo_path: cargo_path(),
    352             cargo_home: None,
    353             color: false,
    354             default_toolchain: false,
    355             allow_implied_features: false,
    356             ignore_compile_errors: false,
    357             ignore_msrv: false,
    358             progress: false,
    359             skip_msrv: false,
    360             summary: false,
    361             ignore_features: Vec::new(),
    362         }
    363     }
    364 }
    365 /// Controls if `cargo test -- --ignored` or `cargo test --include-ignored` should be run.
    366 #[cfg_attr(test, derive(Debug, PartialEq))]
    367 #[derive(Clone, Copy, Default)]
    368 pub(crate) enum Ignored {
    369     /// Don't run any `ignore` tests.
    370     #[default]
    371     None,
    372     /// Only run `ignore` tests.
    373     Only,
    374     /// Run all tests.
    375     Include,
    376 }
    377 /// Positive `usize` or `usize::MAX + 1`.
    378 ///
    379 /// Since we don't use 0, it's repurposed as `usize::MAX + 1`.
    380 #[cfg_attr(test, derive(Debug, PartialEq))]
    381 #[derive(Clone, Copy)]
    382 pub(crate) struct NonZeroUsizePlus1(usize);
    383 impl NonZeroUsizePlus1 {
    384     /// Returns `Self` containing `val`.
    385     ///
    386     /// Note calling code must know that `0` is treated liked
    387     /// `usize::MAX + 1`.
    388     pub(crate) const fn new(val: usize) -> Self {
    389         Self(val)
    390     }
    391 }
    392 impl Display for NonZeroUsizePlus1 {
    393     #[expect(unsafe_code, reason = "comment justifies correctness")]
    394     #[expect(
    395         clippy::arithmetic_side_effects,
    396         reason = "comment justifies correctness"
    397     )]
    398     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    399         /// Helper for `unlikely`.
    400         #[inline(always)]
    401         #[cold]
    402         const fn cold_path() {}
    403         /// Hint that a branch is unlikely.
    404         #[expect(
    405             clippy::inline_always,
    406             reason = "purpose is for the compiler to not optimize"
    407         )]
    408         #[inline(always)]
    409         const fn unlikely(b: bool) -> bool {
    410             if b {
    411                 cold_path();
    412                 true
    413             } else {
    414                 false
    415             }
    416         }
    417         if unlikely(self.0 == 0) {
    418             let mut val = usize::MAX.to_string();
    419             // This won't underflow since the length is at least 1.
    420             let idx = val.len() - 1;
    421             // SAFETY:
    422             // 2^n is even for all n != 0. When n = 0, 2^n = 1. This means we can always increment the last
    423             // digit without carrying.
    424             // We only mutate the last digit which is guaranteed to be valid ASCII; thus we can increment
    425             // the `u8` since digits are consecutive in ASCII.
    426             *unsafe { val.as_bytes_mut() }.index_mut(idx) += 1;
    427             write!(f, "{val}")
    428         } else {
    429             write!(f, "{}", self.0)
    430         }
    431     }
    432 }
    433 /// Progress tracker for when `--progress` was passed.
    434 struct Progress<'package, 'toolchain> {
    435     /// The name of the package.
    436     package: &'package str,
    437     /// The current toolchain counter.
    438     toolchain_counter: &'static str,
    439     /// The total toolchains that will be used.
    440     toolchain_total: &'static str,
    441     /// `"cargo"` or `"cargo "`.
    442     ///
    443     /// Exists for consistent formatting with [`Self::toolchain`].
    444     cargo_cmd: &'toolchain str,
    445     /// The current toolchain.
    446     toolchain: &'toolchain str,
    447     /// The current command counter.
    448     cmd_counter: &'static str,
    449     /// The total commands that will be used.
    450     cmd_total: &'static str,
    451     /// The current command.
    452     cmd: &'static str,
    453     /// The total number of features in the power set.
    454     features_total: String,
    455     /// The time in which we started.
    456     time_started: Instant,
    457     /// `stdout` stream.
    458     ///
    459     /// None iff we encountered any error when writing
    460     /// to it.
    461     stdout: Option<StdoutLock<'static>>,
    462 }
    463 impl<'package> Progress<'package, '_> {
    464     /// Returns `Self` based on running check.
    465     fn check(
    466         package: &'package str,
    467         toolchain: Toolchain<'_>,
    468         use_msrv: bool,
    469         features_total: NonZeroUsizePlus1,
    470     ) -> Self {
    471         Self::inner_new(package, CHECK, ONE, toolchain, use_msrv, features_total)
    472     }
    473     /// Returns `Self` based on running clippy.
    474     fn clippy(
    475         package: &'package str,
    476         toolchain: Toolchain<'_>,
    477         use_msrv: bool,
    478         features_total: NonZeroUsizePlus1,
    479     ) -> Self {
    480         Self::inner_new(package, CLIPPY, ONE, toolchain, use_msrv, features_total)
    481     }
    482     /// Returns `Self` based on running test.
    483     fn test(
    484         package: &'package str,
    485         toolchain: Toolchain<'_>,
    486         use_msrv: bool,
    487         features_total: NonZeroUsizePlus1,
    488         all_targets: bool,
    489     ) -> Self {
    490         if all_targets {
    491             Self::inner_new(
    492                 package,
    493                 TEST_ALL_TARGETS,
    494                 TWO,
    495                 toolchain,
    496                 use_msrv,
    497                 features_total,
    498             )
    499         } else {
    500             Self::inner_new(package, TEST, ONE, toolchain, use_msrv, features_total)
    501         }
    502     }
    503     /// Returns `Self` based on running both check and clippy.
    504     fn check_clippy(
    505         package: &'package str,
    506         toolchain: Toolchain<'_>,
    507         use_msrv: bool,
    508         features_total: NonZeroUsizePlus1,
    509     ) -> Self {
    510         Self::inner_new(package, CHECK, TWO, toolchain, use_msrv, features_total)
    511     }
    512     /// Returns `Self` based on running both check and test.
    513     fn check_test(
    514         package: &'package str,
    515         toolchain: Toolchain<'_>,
    516         use_msrv: bool,
    517         features_total: NonZeroUsizePlus1,
    518         test_all_targets: bool,
    519     ) -> Self {
    520         if test_all_targets {
    521             Self::inner_new(package, CHECK, THREE, toolchain, use_msrv, features_total)
    522         } else {
    523             Self::inner_new(package, CHECK, TWO, toolchain, use_msrv, features_total)
    524         }
    525     }
    526     /// Returns `Self` based on running both clippy and test.
    527     fn clippy_test(
    528         package: &'package str,
    529         toolchain: Toolchain<'_>,
    530         use_msrv: bool,
    531         features_total: NonZeroUsizePlus1,
    532         test_all_targets: bool,
    533     ) -> Self {
    534         if test_all_targets {
    535             Self::inner_new(package, CLIPPY, THREE, toolchain, use_msrv, features_total)
    536         } else {
    537             Self::inner_new(package, CLIPPY, TWO, toolchain, use_msrv, features_total)
    538         }
    539     }
    540     /// Returns `Self` based on running check, clippy, and test.
    541     fn check_clippy_test(
    542         package: &'package str,
    543         toolchain: Toolchain<'_>,
    544         use_msrv: bool,
    545         features_total: NonZeroUsizePlus1,
    546         test_all_targets: bool,
    547     ) -> Self {
    548         if test_all_targets {
    549             Self::inner_new(package, CHECK, FOUR, toolchain, use_msrv, features_total)
    550         } else {
    551             Self::inner_new(package, CHECK, THREE, toolchain, use_msrv, features_total)
    552         }
    553     }
    554     /// Returns `Self` based on the passed arguments.
    555     fn inner_new(
    556         package: &'package str,
    557         cmd: &'static str,
    558         cmd_total: &'static str,
    559         tool: Toolchain<'_>,
    560         use_msrv: bool,
    561         features_total: NonZeroUsizePlus1,
    562     ) -> Self {
    563         let (cargo_cmd, toolchain) = if matches!(tool, Toolchain::Stable) {
    564             (CARGO_SPACE, "+stable")
    565         } else {
    566             (CARGO, "")
    567         };
    568         Self {
    569             package,
    570             toolchain_counter: ONE,
    571             toolchain_total: if use_msrv { TWO } else { ONE },
    572             cargo_cmd,
    573             toolchain,
    574             cmd_counter: ONE,
    575             cmd_total,
    576             cmd,
    577             features_total: features_total.to_string(),
    578             time_started: Instant::now(),
    579             stdout: Some(io::stdout().lock()),
    580         }
    581     }
    582     /// Writes the progress so far to `stdout`.
    583     ///
    584     /// If writing to `stdout` errors, then `stdout` will never be written to.
    585     fn write_to_stdout(
    586         &mut self,
    587         features: &str,
    588         features_counter: usize,
    589         features_skipped: usize,
    590     ) {
    591         if let Some(ref mut std) = self.stdout {
    592             // Example:
    593             // "Package: foo. Toolchain (1/2): cargo +stable. Features (18/128, 3 skipped): foo,bar. Command (1/2): clippy. Time running: 49 s.");
    594             // Note `features_skipped` maxes at `usize::MAX` since the empty set is never skipped.
    595             if writeln!(std, "Package: {}. Toolchain ({}/{}): {}{}. Features ({}/{}, {} skipped): {}. Command ({}/{}): {}. Time running: {} s.", self.package, self.toolchain_counter, self.toolchain_total, self.cargo_cmd, self.toolchain, NonZeroUsizePlus1(features_counter), self.features_total, features_skipped, if features.is_empty() { "<none>" } else { features }, self.cmd_counter, self.cmd_total, self.cmd, self.time_started.elapsed().as_secs()).is_err() {
    596                 drop(self.stdout.take());
    597             }
    598         }
    599     }
    600 }
    601 /// Target selection.
    602 #[cfg_attr(test, derive(Debug, PartialEq))]
    603 #[derive(Clone, Copy)]
    604 pub(crate) enum Target {
    605     /// `--benches`.
    606     Benches,
    607     /// `--bins`.
    608     Bins,
    609     /// `--examples`.
    610     Examples,
    611     /// `--lib`.
    612     Lib,
    613     /// `--tests`.
    614     Tests,
    615 }
    616 impl Target {
    617     /// Transfoms `self` into a `u8`.
    618     const fn to_u8(self) -> u8 {
    619         match self {
    620             Self::Benches => 1,
    621             Self::Bins => 2,
    622             Self::Examples => 4,
    623             Self::Lib => 8,
    624             Self::Tests => 16,
    625         }
    626     }
    627     /// Returns `self` as an `OsString`.
    628     fn to_os_string(self) -> OsString {
    629         match match self {
    630             Self::Benches => BENCHES,
    631             Self::Bins => BINS,
    632             Self::Examples => EXAMPLES,
    633             Self::Lib => LIB,
    634             Self::Tests => TESTS,
    635         }
    636         .parse()
    637         {
    638             Ok(val) => val,
    639             Err(e) => match e {},
    640         }
    641     }
    642 }
    643 /// Combination of targets to select.
    644 #[cfg_attr(test, derive(Debug, PartialEq))]
    645 #[derive(Clone, Copy, Default)]
    646 pub(crate) struct Targets(u8);
    647 impl Targets {
    648     /// Returns `Self` only containing `target`.
    649     const fn new(target: Target) -> Self {
    650         Self(target.to_u8())
    651     }
    652     /// Adds `target` to `self` returning `true` iff `target` wasn't added before.
    653     const fn add(&mut self, target: Target) -> bool {
    654         let this = self.0 | target.to_u8();
    655         if this == self.0 {
    656             false
    657         } else {
    658             self.0 = this;
    659             true
    660         }
    661     }
    662     /// Returns `true` iff `self` contains `target`.
    663     pub(super) const fn contains(self, target: Target) -> bool {
    664         let val = target.to_u8();
    665         self.0 & val == val
    666     }
    667 }
    668 /// Test targets to select.
    669 #[cfg_attr(test, derive(Debug, PartialEq))]
    670 #[derive(Clone, Copy)]
    671 pub(crate) enum CheckClippyTargets {
    672     /// Use the default targets.
    673     Default,
    674     /// `--all-targets`.
    675     All,
    676     /// All targets.
    677     Targets(Targets),
    678 }
    679 /// Test targets to select.
    680 #[cfg_attr(test, derive(Debug, PartialEq))]
    681 #[derive(Clone, Copy)]
    682 pub(crate) enum TestTargets {
    683     /// Use the default targets.
    684     Default,
    685     /// `--all-targets`.
    686     ///
    687     /// This causes all non-`doc` targets to be run and the `--doc` target/mode to be run as well.
    688     All,
    689     /// Non-`doc` targets.
    690     Targets(Targets),
    691     /// `--doc`.
    692     Doc,
    693 }
    694 /// `cargo` command(s) we should run.
    695 #[cfg_attr(test, derive(Debug, PartialEq))]
    696 #[derive(Clone, Copy)]
    697 pub(crate) enum Cmd {
    698     /// `cargo check`.
    699     Check(CheckClippyTargets),
    700     /// `cargo clippy`.
    701     ///
    702     /// The contained `bool` is `true` iff `--deny-warnings` was passed.
    703     Clippy(CheckClippyTargets, bool),
    704     /// `cargo test`.
    705     Test(TestTargets, Ignored),
    706     /// [`Self::Check`] and [`Self::Clippy`].
    707     CheckClippy(CheckClippyTargets, CheckClippyTargets, bool),
    708     /// [`Self::Check`] and [`Self::Test`].
    709     CheckTest(CheckClippyTargets, TestTargets, Ignored),
    710     /// [`Self::Clippy`] and [`Self::Test`].
    711     ClippyTest(CheckClippyTargets, bool, TestTargets, Ignored),
    712     /// [`Self::Check`], [`Self::Clippy`], and [`Self::Test`].
    713     CheckClippyTest(
    714         CheckClippyTargets,
    715         CheckClippyTargets,
    716         bool,
    717         TestTargets,
    718         Ignored,
    719     ),
    720 }
    721 impl Cmd {
    722     /// Runs the appropriate `cargo` command(s) for all features in `power_set`.
    723     ///
    724     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
    725     /// later used.
    726     #[expect(clippy::too_many_lines, reason = "long match expression")]
    727     pub(crate) fn run<'a>(
    728         self,
    729         options: Options<'a, '_, '_>,
    730         msrv: Option<&'a str>,
    731         power_set: &mut PowerSet<'_>,
    732         progress: bool,
    733     ) -> Result<(), Box<CargoErr>> {
    734         match self {
    735             Self::Check(targets) => Self::run_check(
    736                 progress.then(|| {
    737                     Progress::check(
    738                         options.package_name,
    739                         options.toolchain,
    740                         msrv.is_some(),
    741                         power_set.len(),
    742                     )
    743                 }),
    744                 msrv,
    745                 options,
    746                 targets,
    747                 power_set,
    748             ),
    749             Self::Clippy(targets, deny_warnings) => Self::run_clippy(
    750                 progress.then(|| {
    751                     Progress::clippy(
    752                         options.package_name,
    753                         options.toolchain,
    754                         msrv.is_some(),
    755                         power_set.len(),
    756                     )
    757                 }),
    758                 msrv,
    759                 options,
    760                 targets,
    761                 deny_warnings,
    762                 power_set,
    763             ),
    764             Self::Test(targets, ignored) => {
    765                 if matches!(targets, TestTargets::All) {
    766                     Self::run_test_all_targets(
    767                         progress.then(|| {
    768                             Progress::test(
    769                                 options.package_name,
    770                                 options.toolchain,
    771                                 msrv.is_some(),
    772                                 power_set.len(),
    773                                 true,
    774                             )
    775                         }),
    776                         msrv,
    777                         options,
    778                         ignored,
    779                         power_set,
    780                     )
    781                 } else {
    782                     Self::run_test(
    783                         progress.then(|| {
    784                             Progress::test(
    785                                 options.package_name,
    786                                 options.toolchain,
    787                                 msrv.is_some(),
    788                                 power_set.len(),
    789                                 false,
    790                             )
    791                         }),
    792                         msrv,
    793                         options,
    794                         targets,
    795                         ignored,
    796                         power_set,
    797                     )
    798                 }
    799             }
    800             Self::CheckClippy(check_targets, clippy_targets, deny_warnings) => {
    801                 Self::run_check_clippy(
    802                     progress.then(|| {
    803                         Progress::check_clippy(
    804                             options.package_name,
    805                             options.toolchain,
    806                             msrv.is_some(),
    807                             power_set.len(),
    808                         )
    809                     }),
    810                     msrv,
    811                     options,
    812                     check_targets,
    813                     (clippy_targets, deny_warnings),
    814                     power_set,
    815                 )
    816             }
    817             Self::CheckTest(check_targets, test_targets, ignored) => {
    818                 if matches!(test_targets, TestTargets::All) {
    819                     Self::run_check_test_all_targets(
    820                         progress.then(|| {
    821                             Progress::check_test(
    822                                 options.package_name,
    823                                 options.toolchain,
    824                                 msrv.is_some(),
    825                                 power_set.len(),
    826                                 true,
    827                             )
    828                         }),
    829                         msrv,
    830                         options,
    831                         check_targets,
    832                         ignored,
    833                         power_set,
    834                     )
    835                 } else {
    836                     Self::run_check_test(
    837                         progress.then(|| {
    838                             Progress::check_test(
    839                                 options.package_name,
    840                                 options.toolchain,
    841                                 msrv.is_some(),
    842                                 power_set.len(),
    843                                 false,
    844                             )
    845                         }),
    846                         msrv,
    847                         options,
    848                         check_targets,
    849                         (test_targets, ignored),
    850                         power_set,
    851                     )
    852                 }
    853             }
    854             Self::ClippyTest(clippy_targets, deny_warnings, test_targets, ignored) => {
    855                 if matches!(test_targets, TestTargets::All) {
    856                     Self::run_clippy_test_all_targets(
    857                         progress.then(|| {
    858                             Progress::clippy_test(
    859                                 options.package_name,
    860                                 options.toolchain,
    861                                 msrv.is_some(),
    862                                 power_set.len(),
    863                                 true,
    864                             )
    865                         }),
    866                         msrv,
    867                         options,
    868                         (clippy_targets, deny_warnings),
    869                         ignored,
    870                         power_set,
    871                     )
    872                 } else {
    873                     Self::run_clippy_test(
    874                         progress.then(|| {
    875                             Progress::clippy_test(
    876                                 options.package_name,
    877                                 options.toolchain,
    878                                 msrv.is_some(),
    879                                 power_set.len(),
    880                                 false,
    881                             )
    882                         }),
    883                         msrv,
    884                         options,
    885                         (clippy_targets, deny_warnings),
    886                         (test_targets, ignored),
    887                         power_set,
    888                     )
    889                 }
    890             }
    891             Self::CheckClippyTest(
    892                 check_targets,
    893                 clippy_targets,
    894                 deny_warnings,
    895                 test_targets,
    896                 ignored,
    897             ) => {
    898                 if matches!(test_targets, TestTargets::All) {
    899                     Self::run_check_clippy_test_all_targets(
    900                         progress.then(|| {
    901                             Progress::check_clippy_test(
    902                                 options.package_name,
    903                                 options.toolchain,
    904                                 msrv.is_some(),
    905                                 power_set.len(),
    906                                 true,
    907                             )
    908                         }),
    909                         msrv,
    910                         options,
    911                         check_targets,
    912                         (clippy_targets, deny_warnings),
    913                         ignored,
    914                         power_set,
    915                     )
    916                 } else {
    917                     Self::run_check_clippy_test(
    918                         progress.then(|| {
    919                             Progress::check_clippy_test(
    920                                 options.package_name,
    921                                 options.toolchain,
    922                                 msrv.is_some(),
    923                                 power_set.len(),
    924                                 false,
    925                             )
    926                         }),
    927                         msrv,
    928                         options,
    929                         check_targets,
    930                         (clippy_targets, deny_warnings),
    931                         (test_targets, ignored),
    932                         power_set,
    933                     )
    934                 }
    935             }
    936         }
    937     }
    938     /// Runs `cargo check` for all features in `power_set`.
    939     ///
    940     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
    941     /// later used.
    942     fn run_check<'a>(
    943         mut progress: Option<Progress<'_, 'a>>,
    944         msrv: Option<&'a str>,
    945         mut options: Options<'a, '_, '_>,
    946         targets: CheckClippyTargets,
    947         power_set: &mut PowerSet<'_>,
    948     ) -> Result<(), Box<CargoErr>> {
    949         /// Runs the commands for each feature combination.
    950         fn run_loop<'a>(
    951             progress: &mut Option<Progress<'_, 'a>>,
    952             options: &mut Options<'a, '_, '_>,
    953             targets: CheckClippyTargets,
    954             power_set: &mut PowerSet<'_>,
    955         ) -> Result<(), Box<CargoErr>> {
    956             let mut feat_counter = 1usize;
    957             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
    958             // cause some bloat though.
    959             if let Some(ref mut prog) = *progress {
    960                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
    961                     prog.cmd_counter = ONE;
    962                     prog.cmd = CHECK;
    963                     prog.write_to_stdout(set, feat_counter, skip_count);
    964                     Check::run(options, targets, set)?;
    965                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
    966                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
    967                     // [`NonZeroUsizePlus1::fmt`].
    968                     feat_counter = feat_counter.wrapping_add(1);
    969                 }
    970             } else {
    971                 while let Some(set) = power_set.next_set() {
    972                     Check::run(options, targets, set)?;
    973                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
    974                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
    975                     // [`NonZeroUsizePlus1::fmt`].
    976                     feat_counter = feat_counter.wrapping_add(1);
    977                 }
    978             }
    979             Ok(())
    980         }
    981         run_loop(&mut progress, &mut options, targets, power_set).and_then(|()| {
    982             if let Some(msrv_val) = msrv {
    983                 if let Some(ref mut prog) = progress {
    984                     prog.toolchain_counter = TWO;
    985                     prog.cargo_cmd = CARGO_SPACE;
    986                     prog.toolchain = msrv_val;
    987                 }
    988                 options.toolchain = Toolchain::Msrv(msrv_val);
    989                 power_set.reset();
    990                 run_loop(&mut progress, &mut options, targets, power_set)
    991             } else {
    992                 Ok(())
    993             }
    994         })
    995     }
    996     /// Runs `cargo clippy` for all features in `power_set`.
    997     ///
    998     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
    999     /// later used.
   1000     fn run_clippy<'a>(
   1001         mut progress: Option<Progress<'_, 'a>>,
   1002         msrv: Option<&'a str>,
   1003         mut options: Options<'a, '_, '_>,
   1004         targets: CheckClippyTargets,
   1005         deny_warnings: bool,
   1006         power_set: &mut PowerSet<'_>,
   1007     ) -> Result<(), Box<CargoErr>> {
   1008         /// Runs the commands for each feature combination.
   1009         fn run_loop<'a>(
   1010             progress: &mut Option<Progress<'_, 'a>>,
   1011             options: &mut Options<'a, '_, '_>,
   1012             targets: CheckClippyTargets,
   1013             deny_warnings: bool,
   1014             power_set: &mut PowerSet<'_>,
   1015         ) -> Result<(), Box<CargoErr>> {
   1016             let mut feat_counter = 1usize;
   1017             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1018             // cause some bloat though.
   1019             if let Some(ref mut prog) = *progress {
   1020                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1021                     prog.cmd_counter = ONE;
   1022                     prog.cmd = CLIPPY;
   1023                     prog.write_to_stdout(set, feat_counter, skip_count);
   1024                     Clippy::run(options, targets, deny_warnings, set)?;
   1025                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1026                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1027                     // [`NonZeroUsizePlus1::fmt`].
   1028                     feat_counter = feat_counter.wrapping_add(1);
   1029                 }
   1030             } else {
   1031                 while let Some(set) = power_set.next_set() {
   1032                     Clippy::run(options, targets, deny_warnings, set)?;
   1033                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1034                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1035                     // [`NonZeroUsizePlus1::fmt`].
   1036                     feat_counter = feat_counter.wrapping_add(1);
   1037                 }
   1038             }
   1039             Ok(())
   1040         }
   1041         run_loop(
   1042             &mut progress,
   1043             &mut options,
   1044             targets,
   1045             deny_warnings,
   1046             power_set,
   1047         )
   1048         .and_then(|()| {
   1049             if let Some(msrv_val) = msrv {
   1050                 if let Some(ref mut prog) = progress {
   1051                     prog.toolchain_counter = TWO;
   1052                     prog.cargo_cmd = CARGO_SPACE;
   1053                     prog.toolchain = msrv_val;
   1054                 }
   1055                 options.toolchain = Toolchain::Msrv(msrv_val);
   1056                 power_set.reset();
   1057                 run_loop(
   1058                     &mut progress,
   1059                     &mut options,
   1060                     targets,
   1061                     deny_warnings,
   1062                     power_set,
   1063                 )
   1064             } else {
   1065                 Ok(())
   1066             }
   1067         })
   1068     }
   1069     /// Runs `cargo test` for all features in `power_set`.
   1070     ///
   1071     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1072     /// later used.
   1073     fn run_test<'a>(
   1074         mut progress: Option<Progress<'_, 'a>>,
   1075         msrv: Option<&'a str>,
   1076         mut options: Options<'a, '_, '_>,
   1077         targets: TestTargets,
   1078         ignored: Ignored,
   1079         power_set: &mut PowerSet<'_>,
   1080     ) -> Result<(), Box<CargoErr>> {
   1081         /// Runs the commands for each feature combination.
   1082         fn run_loop<'a>(
   1083             progress: &mut Option<Progress<'_, 'a>>,
   1084             options: &mut Options<'a, '_, '_>,
   1085             targets: TestTargets,
   1086             ignored: Ignored,
   1087             power_set: &mut PowerSet<'_>,
   1088         ) -> Result<(), Box<CargoErr>> {
   1089             let mut feat_counter = 1usize;
   1090             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1091             // cause some bloat though.
   1092             if let Some(ref mut prog) = *progress {
   1093                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1094                     prog.cmd_counter = ONE;
   1095                     prog.cmd = TEST;
   1096                     prog.write_to_stdout(set, feat_counter, skip_count);
   1097                     Test::run(options, targets, ignored, set)?;
   1098                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1099                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1100                     // [`NonZeroUsizePlus1::fmt`].
   1101                     feat_counter = feat_counter.wrapping_add(1);
   1102                 }
   1103             } else {
   1104                 while let Some(set) = power_set.next_set() {
   1105                     Test::run(options, targets, ignored, set)?;
   1106                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1107                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1108                     // [`NonZeroUsizePlus1::fmt`].
   1109                     feat_counter = feat_counter.wrapping_add(1);
   1110                 }
   1111             }
   1112             Ok(())
   1113         }
   1114         run_loop(&mut progress, &mut options, targets, ignored, power_set).and_then(|()| {
   1115             if let Some(msrv_val) = msrv {
   1116                 if let Some(ref mut prog) = progress {
   1117                     prog.toolchain_counter = TWO;
   1118                     prog.cargo_cmd = CARGO_SPACE;
   1119                     prog.toolchain = msrv_val;
   1120                 }
   1121                 options.toolchain = Toolchain::Msrv(msrv_val);
   1122                 power_set.reset();
   1123                 run_loop(&mut progress, &mut options, targets, ignored, power_set)
   1124             } else {
   1125                 Ok(())
   1126             }
   1127         })
   1128     }
   1129     /// Runs `cargo test --all-targets` and `cargo test --doc` for all features in `power_set`.
   1130     ///
   1131     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1132     /// later used.
   1133     fn run_test_all_targets<'a>(
   1134         mut progress: Option<Progress<'_, 'a>>,
   1135         msrv: Option<&'a str>,
   1136         mut options: Options<'a, '_, '_>,
   1137         ignored: Ignored,
   1138         power_set: &mut PowerSet<'_>,
   1139     ) -> Result<(), Box<CargoErr>> {
   1140         /// Runs the commands for each feature combination.
   1141         fn run_loop<'a>(
   1142             progress: &mut Option<Progress<'_, 'a>>,
   1143             options: &mut Options<'a, '_, '_>,
   1144             ignored: Ignored,
   1145             power_set: &mut PowerSet<'_>,
   1146         ) -> Result<(), Box<CargoErr>> {
   1147             let mut feat_counter = 1usize;
   1148             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1149             // cause some bloat though.
   1150             if let Some(ref mut prog) = *progress {
   1151                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1152                     prog.cmd_counter = ONE;
   1153                     prog.cmd = TEST_ALL_TARGETS;
   1154                     prog.write_to_stdout(set, feat_counter, skip_count);
   1155                     Test::run(options, TestTargets::All, ignored, set).and_then(|()| {
   1156                         prog.cmd_counter = TWO;
   1157                         prog.cmd = TEST_DOC;
   1158                         prog.write_to_stdout(set, feat_counter, skip_count);
   1159                         Test::run(options, TestTargets::Doc, ignored, set)
   1160                     })?;
   1161                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1162                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1163                     // [`NonZeroUsizePlus1::fmt`].
   1164                     feat_counter = feat_counter.wrapping_add(1);
   1165                 }
   1166             } else {
   1167                 while let Some(set) = power_set.next_set() {
   1168                     Test::run(options, TestTargets::All, ignored, set)
   1169                         .and_then(|()| Test::run(options, TestTargets::Doc, ignored, set))?;
   1170                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1171                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1172                     // [`NonZeroUsizePlus1::fmt`].
   1173                     feat_counter = feat_counter.wrapping_add(1);
   1174                 }
   1175             }
   1176             Ok(())
   1177         }
   1178         run_loop(&mut progress, &mut options, ignored, power_set).and_then(|()| {
   1179             if let Some(msrv_val) = msrv {
   1180                 if let Some(ref mut prog) = progress {
   1181                     prog.toolchain_counter = TWO;
   1182                     prog.cargo_cmd = CARGO_SPACE;
   1183                     prog.toolchain = msrv_val;
   1184                 }
   1185                 options.toolchain = Toolchain::Msrv(msrv_val);
   1186                 power_set.reset();
   1187                 run_loop(&mut progress, &mut options, ignored, power_set)
   1188             } else {
   1189                 Ok(())
   1190             }
   1191         })
   1192     }
   1193     /// Runs `cargo check` and `cargo clippy` for all features in `power_set`.
   1194     ///
   1195     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1196     /// later used.
   1197     fn run_check_clippy<'a>(
   1198         mut progress: Option<Progress<'_, 'a>>,
   1199         msrv: Option<&'a str>,
   1200         mut options: Options<'a, '_, '_>,
   1201         check_targets: CheckClippyTargets,
   1202         clippy_info: (CheckClippyTargets, bool),
   1203         power_set: &mut PowerSet<'_>,
   1204     ) -> Result<(), Box<CargoErr>> {
   1205         /// Runs the commands for each feature combination.
   1206         fn run_loop<'a>(
   1207             progress: &mut Option<Progress<'_, 'a>>,
   1208             options: &mut Options<'a, '_, '_>,
   1209             check_targets: CheckClippyTargets,
   1210             (clippy_targets, deny_warnings): (CheckClippyTargets, bool),
   1211             power_set: &mut PowerSet<'_>,
   1212         ) -> Result<(), Box<CargoErr>> {
   1213             let mut feat_counter = 1usize;
   1214             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1215             // cause some bloat though.
   1216             if let Some(ref mut prog) = *progress {
   1217                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1218                     prog.cmd_counter = ONE;
   1219                     prog.cmd = CHECK;
   1220                     prog.write_to_stdout(set, feat_counter, skip_count);
   1221                     Check::run(options, check_targets, set).and_then(|()| {
   1222                         prog.cmd_counter = TWO;
   1223                         prog.cmd = CLIPPY;
   1224                         prog.write_to_stdout(set, feat_counter, skip_count);
   1225                         Clippy::run(options, clippy_targets, deny_warnings, set)
   1226                     })?;
   1227                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1228                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1229                     // [`NonZeroUsizePlus1::fmt`].
   1230                     feat_counter = feat_counter.wrapping_add(1);
   1231                 }
   1232             } else {
   1233                 while let Some(set) = power_set.next_set() {
   1234                     Check::run(options, check_targets, set)
   1235                         .and_then(|()| Clippy::run(options, clippy_targets, deny_warnings, set))?;
   1236                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1237                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1238                     // [`NonZeroUsizePlus1::fmt`].
   1239                     feat_counter = feat_counter.wrapping_add(1);
   1240                 }
   1241             }
   1242             Ok(())
   1243         }
   1244         run_loop(
   1245             &mut progress,
   1246             &mut options,
   1247             check_targets,
   1248             clippy_info,
   1249             power_set,
   1250         )
   1251         .and_then(|()| {
   1252             if let Some(msrv_val) = msrv {
   1253                 if let Some(ref mut prog) = progress {
   1254                     prog.toolchain_counter = TWO;
   1255                     prog.cargo_cmd = CARGO_SPACE;
   1256                     prog.toolchain = msrv_val;
   1257                 }
   1258                 options.toolchain = Toolchain::Msrv(msrv_val);
   1259                 power_set.reset();
   1260                 run_loop(
   1261                     &mut progress,
   1262                     &mut options,
   1263                     check_targets,
   1264                     clippy_info,
   1265                     power_set,
   1266                 )
   1267             } else {
   1268                 Ok(())
   1269             }
   1270         })
   1271     }
   1272     /// Runs `cargo check` and `cargo test` for all features in `power_set`.
   1273     ///
   1274     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1275     /// later used.
   1276     fn run_check_test<'a>(
   1277         mut progress: Option<Progress<'_, 'a>>,
   1278         msrv: Option<&'a str>,
   1279         mut options: Options<'a, '_, '_>,
   1280         check_targets: CheckClippyTargets,
   1281         test_info: (TestTargets, Ignored),
   1282         power_set: &mut PowerSet<'_>,
   1283     ) -> Result<(), Box<CargoErr>> {
   1284         /// Runs the commands for each feature combination.
   1285         fn run_loop<'a>(
   1286             progress: &mut Option<Progress<'_, 'a>>,
   1287             options: &mut Options<'a, '_, '_>,
   1288             check_targets: CheckClippyTargets,
   1289             (test_targets, ignored): (TestTargets, Ignored),
   1290             power_set: &mut PowerSet<'_>,
   1291         ) -> Result<(), Box<CargoErr>> {
   1292             let mut feat_counter = 1usize;
   1293             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1294             // cause some bloat though.
   1295             if let Some(ref mut prog) = *progress {
   1296                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1297                     prog.cmd_counter = ONE;
   1298                     prog.cmd = CHECK;
   1299                     prog.write_to_stdout(set, feat_counter, skip_count);
   1300                     Check::run(options, check_targets, set).and_then(|()| {
   1301                         prog.cmd_counter = TWO;
   1302                         prog.cmd = TEST;
   1303                         prog.write_to_stdout(set, feat_counter, skip_count);
   1304                         Test::run(options, test_targets, ignored, set)
   1305                     })?;
   1306                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1307                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1308                     // [`NonZeroUsizePlus1::fmt`].
   1309                     feat_counter = feat_counter.wrapping_add(1);
   1310                 }
   1311             } else {
   1312                 while let Some(set) = power_set.next_set() {
   1313                     Check::run(options, check_targets, set)
   1314                         .and_then(|()| Test::run(options, test_targets, ignored, set))?;
   1315                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1316                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1317                     // [`NonZeroUsizePlus1::fmt`].
   1318                     feat_counter = feat_counter.wrapping_add(1);
   1319                 }
   1320             }
   1321             Ok(())
   1322         }
   1323         run_loop(
   1324             &mut progress,
   1325             &mut options,
   1326             check_targets,
   1327             test_info,
   1328             power_set,
   1329         )
   1330         .and_then(|()| {
   1331             if let Some(msrv_val) = msrv {
   1332                 if let Some(ref mut prog) = progress {
   1333                     prog.toolchain_counter = TWO;
   1334                     prog.cargo_cmd = CARGO_SPACE;
   1335                     prog.toolchain = msrv_val;
   1336                 }
   1337                 options.toolchain = Toolchain::Msrv(msrv_val);
   1338                 power_set.reset();
   1339                 run_loop(
   1340                     &mut progress,
   1341                     &mut options,
   1342                     check_targets,
   1343                     test_info,
   1344                     power_set,
   1345                 )
   1346             } else {
   1347                 Ok(())
   1348             }
   1349         })
   1350     }
   1351     /// Runs `cargo check`, `cargo test --all-targets`, and `cargo test --doc` for all features in `power_set`.
   1352     ///
   1353     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1354     /// later used.
   1355     fn run_check_test_all_targets<'a>(
   1356         mut progress: Option<Progress<'_, 'a>>,
   1357         msrv: Option<&'a str>,
   1358         mut options: Options<'a, '_, '_>,
   1359         check_targets: CheckClippyTargets,
   1360         ignored: Ignored,
   1361         power_set: &mut PowerSet<'_>,
   1362     ) -> Result<(), Box<CargoErr>> {
   1363         /// Runs the commands for each feature combination.
   1364         fn run_loop<'a>(
   1365             progress: &mut Option<Progress<'_, 'a>>,
   1366             options: &mut Options<'a, '_, '_>,
   1367             check_targets: CheckClippyTargets,
   1368             ignored: Ignored,
   1369             power_set: &mut PowerSet<'_>,
   1370         ) -> Result<(), Box<CargoErr>> {
   1371             let mut feat_counter = 1usize;
   1372             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1373             // cause some bloat though.
   1374             if let Some(ref mut prog) = *progress {
   1375                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1376                     prog.cmd_counter = ONE;
   1377                     prog.cmd = CHECK;
   1378                     prog.write_to_stdout(set, feat_counter, skip_count);
   1379                     Check::run(options, check_targets, set).and_then(|()| {
   1380                         prog.cmd_counter = TWO;
   1381                         prog.cmd = TEST_ALL_TARGETS;
   1382                         prog.write_to_stdout(set, feat_counter, skip_count);
   1383                         Test::run(options, TestTargets::All, ignored, set).and_then(|()| {
   1384                             prog.cmd_counter = THREE;
   1385                             prog.cmd = TEST_DOC;
   1386                             prog.write_to_stdout(set, feat_counter, skip_count);
   1387                             Test::run(options, TestTargets::Doc, ignored, set)
   1388                         })
   1389                     })?;
   1390                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1391                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1392                     // [`NonZeroUsizePlus1::fmt`].
   1393                     feat_counter = feat_counter.wrapping_add(1);
   1394                 }
   1395             } else {
   1396                 while let Some(set) = power_set.next_set() {
   1397                     Check::run(options, check_targets, set).and_then(|()| {
   1398                         Test::run(options, TestTargets::All, ignored, set)
   1399                             .and_then(|()| Test::run(options, TestTargets::Doc, ignored, set))
   1400                     })?;
   1401                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1402                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1403                     // [`NonZeroUsizePlus1::fmt`].
   1404                     feat_counter = feat_counter.wrapping_add(1);
   1405                 }
   1406             }
   1407             Ok(())
   1408         }
   1409         run_loop(
   1410             &mut progress,
   1411             &mut options,
   1412             check_targets,
   1413             ignored,
   1414             power_set,
   1415         )
   1416         .and_then(|()| {
   1417             if let Some(msrv_val) = msrv {
   1418                 if let Some(ref mut prog) = progress {
   1419                     prog.toolchain_counter = TWO;
   1420                     prog.cargo_cmd = CARGO_SPACE;
   1421                     prog.toolchain = msrv_val;
   1422                 }
   1423                 options.toolchain = Toolchain::Msrv(msrv_val);
   1424                 power_set.reset();
   1425                 run_loop(
   1426                     &mut progress,
   1427                     &mut options,
   1428                     check_targets,
   1429                     ignored,
   1430                     power_set,
   1431                 )
   1432             } else {
   1433                 Ok(())
   1434             }
   1435         })
   1436     }
   1437     /// Runs `cargo clippy` and `cargo test` for all features in `power_set`.
   1438     ///
   1439     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1440     /// later used.
   1441     fn run_clippy_test<'a>(
   1442         mut progress: Option<Progress<'_, 'a>>,
   1443         msrv: Option<&'a str>,
   1444         mut options: Options<'a, '_, '_>,
   1445         clippy_info: (CheckClippyTargets, bool),
   1446         test_info: (TestTargets, Ignored),
   1447         power_set: &mut PowerSet<'_>,
   1448     ) -> Result<(), Box<CargoErr>> {
   1449         /// Runs the commands for each feature combination.
   1450         fn run_loop<'a>(
   1451             progress: &mut Option<Progress<'_, 'a>>,
   1452             options: &mut Options<'a, '_, '_>,
   1453             (clippy_targets, deny_warnings): (CheckClippyTargets, bool),
   1454             (test_targets, ignored): (TestTargets, Ignored),
   1455             power_set: &mut PowerSet<'_>,
   1456         ) -> Result<(), Box<CargoErr>> {
   1457             let mut feat_counter = 1usize;
   1458             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1459             // cause some bloat though.
   1460             if let Some(ref mut prog) = *progress {
   1461                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1462                     prog.cmd_counter = ONE;
   1463                     prog.cmd = CLIPPY;
   1464                     prog.write_to_stdout(set, feat_counter, skip_count);
   1465                     Clippy::run(options, clippy_targets, deny_warnings, set).and_then(|()| {
   1466                         prog.cmd_counter = TWO;
   1467                         prog.cmd = TEST;
   1468                         prog.write_to_stdout(set, feat_counter, skip_count);
   1469                         Test::run(options, test_targets, ignored, set)
   1470                     })?;
   1471                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1472                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1473                     // [`NonZeroUsizePlus1::fmt`].
   1474                     feat_counter = feat_counter.wrapping_add(1);
   1475                 }
   1476             } else {
   1477                 while let Some(set) = power_set.next_set() {
   1478                     Clippy::run(options, clippy_targets, deny_warnings, set)
   1479                         .and_then(|()| Test::run(options, test_targets, ignored, set))?;
   1480                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1481                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1482                     // [`NonZeroUsizePlus1::fmt`].
   1483                     feat_counter = feat_counter.wrapping_add(1);
   1484                 }
   1485             }
   1486             Ok(())
   1487         }
   1488         run_loop(
   1489             &mut progress,
   1490             &mut options,
   1491             clippy_info,
   1492             test_info,
   1493             power_set,
   1494         )
   1495         .and_then(|()| {
   1496             if let Some(msrv_val) = msrv {
   1497                 if let Some(ref mut prog) = progress {
   1498                     prog.toolchain_counter = TWO;
   1499                     prog.cargo_cmd = CARGO_SPACE;
   1500                     prog.toolchain = msrv_val;
   1501                 }
   1502                 options.toolchain = Toolchain::Msrv(msrv_val);
   1503                 power_set.reset();
   1504                 run_loop(
   1505                     &mut progress,
   1506                     &mut options,
   1507                     clippy_info,
   1508                     test_info,
   1509                     power_set,
   1510                 )
   1511             } else {
   1512                 Ok(())
   1513             }
   1514         })
   1515     }
   1516     /// Runs `cargo clippy`, `cargo test --all-targets`, and `cargo test --doc` for all features in `power_set`.
   1517     ///
   1518     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1519     /// later used.
   1520     fn run_clippy_test_all_targets<'a>(
   1521         mut progress: Option<Progress<'_, 'a>>,
   1522         msrv: Option<&'a str>,
   1523         mut options: Options<'a, '_, '_>,
   1524         clippy_info: (CheckClippyTargets, bool),
   1525         ignored: Ignored,
   1526         power_set: &mut PowerSet<'_>,
   1527     ) -> Result<(), Box<CargoErr>> {
   1528         /// Runs the commands for each feature combination.
   1529         fn run_loop<'a>(
   1530             progress: &mut Option<Progress<'_, 'a>>,
   1531             options: &mut Options<'a, '_, '_>,
   1532             (clippy_targets, deny_warnings): (CheckClippyTargets, bool),
   1533             ignored: Ignored,
   1534             power_set: &mut PowerSet<'_>,
   1535         ) -> Result<(), Box<CargoErr>> {
   1536             let mut feat_counter = 1usize;
   1537             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1538             // cause some bloat though.
   1539             if let Some(ref mut prog) = *progress {
   1540                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1541                     prog.cmd_counter = ONE;
   1542                     prog.cmd = CLIPPY;
   1543                     prog.write_to_stdout(set, feat_counter, skip_count);
   1544                     Clippy::run(options, clippy_targets, deny_warnings, set).and_then(|()| {
   1545                         prog.cmd_counter = TWO;
   1546                         prog.cmd = TEST_ALL_TARGETS;
   1547                         prog.write_to_stdout(set, feat_counter, skip_count);
   1548                         Test::run(options, TestTargets::All, ignored, set).and_then(|()| {
   1549                             prog.cmd_counter = THREE;
   1550                             prog.cmd = TEST_DOC;
   1551                             prog.write_to_stdout(set, feat_counter, skip_count);
   1552                             Test::run(options, TestTargets::Doc, ignored, set)
   1553                         })
   1554                     })?;
   1555                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1556                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1557                     // [`NonZeroUsizePlus1::fmt`].
   1558                     feat_counter = feat_counter.wrapping_add(1);
   1559                 }
   1560             } else {
   1561                 while let Some(set) = power_set.next_set() {
   1562                     Clippy::run(options, clippy_targets, deny_warnings, set).and_then(|()| {
   1563                         Test::run(options, TestTargets::All, ignored, set)
   1564                             .and_then(|()| Test::run(options, TestTargets::Doc, ignored, set))
   1565                     })?;
   1566                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1567                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1568                     // [`NonZeroUsizePlus1::fmt`].
   1569                     feat_counter = feat_counter.wrapping_add(1);
   1570                 }
   1571             }
   1572             Ok(())
   1573         }
   1574         run_loop(&mut progress, &mut options, clippy_info, ignored, power_set).and_then(|()| {
   1575             if let Some(msrv_val) = msrv {
   1576                 if let Some(ref mut prog) = progress {
   1577                     prog.toolchain_counter = TWO;
   1578                     prog.cargo_cmd = CARGO_SPACE;
   1579                     prog.toolchain = msrv_val;
   1580                 }
   1581                 options.toolchain = Toolchain::Msrv(msrv_val);
   1582                 power_set.reset();
   1583                 run_loop(&mut progress, &mut options, clippy_info, ignored, power_set)
   1584             } else {
   1585                 Ok(())
   1586             }
   1587         })
   1588     }
   1589     /// Runs `cargo check`, `cargo clippy`, and `cargo test` for all features in `power_set`.
   1590     ///
   1591     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1592     /// later used.
   1593     fn run_check_clippy_test<'a>(
   1594         mut progress: Option<Progress<'_, 'a>>,
   1595         msrv: Option<&'a str>,
   1596         mut options: Options<'a, '_, '_>,
   1597         check_targets: CheckClippyTargets,
   1598         clippy_info: (CheckClippyTargets, bool),
   1599         test_info: (TestTargets, Ignored),
   1600         power_set: &mut PowerSet<'_>,
   1601     ) -> Result<(), Box<CargoErr>> {
   1602         /// Runs the commands for each feature combination.
   1603         fn run_loop<'a>(
   1604             progress: &mut Option<Progress<'_, 'a>>,
   1605             options: &mut Options<'a, '_, '_>,
   1606             check_targets: CheckClippyTargets,
   1607             (clippy_targets, deny_warnings): (CheckClippyTargets, bool),
   1608             (test_targets, ignored): (TestTargets, Ignored),
   1609             power_set: &mut PowerSet<'_>,
   1610         ) -> Result<(), Box<CargoErr>> {
   1611             let mut feat_counter = 1usize;
   1612             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1613             // cause some bloat though.
   1614             if let Some(ref mut prog) = *progress {
   1615                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1616                     prog.cmd_counter = ONE;
   1617                     prog.cmd = CHECK;
   1618                     prog.write_to_stdout(set, feat_counter, skip_count);
   1619                     Check::run(options, check_targets, set).and_then(|()| {
   1620                         prog.cmd_counter = TWO;
   1621                         prog.cmd = CLIPPY;
   1622                         prog.write_to_stdout(set, feat_counter, skip_count);
   1623                         Clippy::run(options, clippy_targets, deny_warnings, set).and_then(|()| {
   1624                             prog.cmd_counter = THREE;
   1625                             prog.cmd = TEST;
   1626                             prog.write_to_stdout(set, feat_counter, skip_count);
   1627                             Test::run(options, test_targets, ignored, set)
   1628                         })
   1629                     })?;
   1630                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1631                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1632                     // [`NonZeroUsizePlus1::fmt`].
   1633                     feat_counter = feat_counter.wrapping_add(1);
   1634                 }
   1635             } else {
   1636                 while let Some(set) = power_set.next_set() {
   1637                     Check::run(options, check_targets, set).and_then(|()| {
   1638                         Clippy::run(options, clippy_targets, deny_warnings, set)
   1639                             .and_then(|()| Test::run(options, test_targets, ignored, set))
   1640                     })?;
   1641                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1642                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1643                     // [`NonZeroUsizePlus1::fmt`].
   1644                     feat_counter = feat_counter.wrapping_add(1);
   1645                 }
   1646             }
   1647             Ok(())
   1648         }
   1649         run_loop(
   1650             &mut progress,
   1651             &mut options,
   1652             check_targets,
   1653             clippy_info,
   1654             test_info,
   1655             power_set,
   1656         )
   1657         .and_then(|()| {
   1658             if let Some(msrv_val) = msrv {
   1659                 if let Some(ref mut prog) = progress {
   1660                     prog.toolchain_counter = TWO;
   1661                     prog.cargo_cmd = CARGO_SPACE;
   1662                     prog.toolchain = msrv_val;
   1663                 }
   1664                 options.toolchain = Toolchain::Msrv(msrv_val);
   1665                 power_set.reset();
   1666                 run_loop(
   1667                     &mut progress,
   1668                     &mut options,
   1669                     check_targets,
   1670                     clippy_info,
   1671                     test_info,
   1672                     power_set,
   1673                 )
   1674             } else {
   1675                 Ok(())
   1676             }
   1677         })
   1678     }
   1679     /// Runs `cargo check`, `cargo clippy`, `cargo test --all-targets`, and `cargo test --doc` for all features
   1680     /// in `power_set`.
   1681     ///
   1682     /// Note the [`Toolchain`] in `options` is first used; and if `msrv.is_some()`, then [`Toolchain::Msrv`] is
   1683     /// later used.
   1684     fn run_check_clippy_test_all_targets<'a>(
   1685         mut progress: Option<Progress<'_, 'a>>,
   1686         msrv: Option<&'a str>,
   1687         mut options: Options<'a, '_, '_>,
   1688         check_targets: CheckClippyTargets,
   1689         clippy_info: (CheckClippyTargets, bool),
   1690         ignored: Ignored,
   1691         power_set: &mut PowerSet<'_>,
   1692     ) -> Result<(), Box<CargoErr>> {
   1693         /// Runs the commands for each feature combination.
   1694         fn run_loop<'a>(
   1695             progress: &mut Option<Progress<'_, 'a>>,
   1696             options: &mut Options<'a, '_, '_>,
   1697             check_targets: CheckClippyTargets,
   1698             (clippy_targets, deny_warnings): (CheckClippyTargets, bool),
   1699             ignored: Ignored,
   1700             power_set: &mut PowerSet<'_>,
   1701         ) -> Result<(), Box<CargoErr>> {
   1702             let mut feat_counter = 1usize;
   1703             // We have two loops instead of one to avoid repeated and unnecessary if branches. This does
   1704             // cause some bloat though.
   1705             if let Some(ref mut prog) = *progress {
   1706                 while let Some((set, skip_count)) = power_set.next_set_with_skip_count() {
   1707                     prog.cmd_counter = ONE;
   1708                     prog.cmd = CHECK;
   1709                     prog.write_to_stdout(set, feat_counter, skip_count);
   1710                     Check::run(options, check_targets, set).and_then(|()| {
   1711                         prog.cmd_counter = TWO;
   1712                         prog.cmd = CLIPPY;
   1713                         prog.write_to_stdout(set, feat_counter, skip_count);
   1714                         Clippy::run(options, clippy_targets, deny_warnings, set).and_then(|()| {
   1715                             prog.cmd_counter = THREE;
   1716                             prog.cmd = TEST_ALL_TARGETS;
   1717                             prog.write_to_stdout(set, feat_counter, skip_count);
   1718                             Test::run(options, TestTargets::All, ignored, set).and_then(|()| {
   1719                                 prog.cmd_counter = FOUR;
   1720                                 prog.cmd = TEST_DOC;
   1721                                 prog.write_to_stdout(set, feat_counter, skip_count);
   1722                                 Test::run(options, TestTargets::Doc, ignored, set)
   1723                             })
   1724                         })
   1725                     })?;
   1726                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1727                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1728                     // [`NonZeroUsizePlus1::fmt`].
   1729                     feat_counter = feat_counter.wrapping_add(1);
   1730                 }
   1731             } else {
   1732                 while let Some(set) = power_set.next_set() {
   1733                     Check::run(options, check_targets, set).and_then(|()| {
   1734                         Clippy::run(options, clippy_targets, deny_warnings, set).and_then(|()| {
   1735                             Test::run(options, TestTargets::All, ignored, set)
   1736                                 .and_then(|()| Test::run(options, TestTargets::Doc, ignored, set))
   1737                         })
   1738                     })?;
   1739                     // The maximum number possible is `usize::MAX + 1`; however that can only happen at the very
   1740                     // last item. Since we never display 0, we treat 0 as `usize::MAX + 1` when we display it via
   1741                     // [`NonZeroUsizePlus1::fmt`].
   1742                     feat_counter = feat_counter.wrapping_add(1);
   1743                 }
   1744             }
   1745             Ok(())
   1746         }
   1747         run_loop(
   1748             &mut progress,
   1749             &mut options,
   1750             check_targets,
   1751             clippy_info,
   1752             ignored,
   1753             power_set,
   1754         )
   1755         .and_then(|()| {
   1756             if let Some(msrv_val) = msrv {
   1757                 if let Some(ref mut prog) = progress {
   1758                     prog.toolchain_counter = TWO;
   1759                     prog.cargo_cmd = CARGO_SPACE;
   1760                     prog.toolchain = msrv_val;
   1761                 }
   1762                 options.toolchain = Toolchain::Msrv(msrv_val);
   1763                 power_set.reset();
   1764                 run_loop(
   1765                     &mut progress,
   1766                     &mut options,
   1767                     check_targets,
   1768                     clippy_info,
   1769                     ignored,
   1770                     power_set,
   1771                 )
   1772             } else {
   1773                 Ok(())
   1774             }
   1775         })
   1776     }
   1777 }
   1778 /// `cargo` command passed.
   1779 enum CargoCmd<'a> {
   1780     /// check.
   1781     Check(&'a mut CheckClippyTargets),
   1782     /// clippy.
   1783     Clippy(&'a mut (CheckClippyTargets, bool)),
   1784     /// test.
   1785     Test(&'a mut (TestTargets, Ignored)),
   1786 }
   1787 impl CargoCmd<'_> {
   1788     /// Adds the `--all-targets` target to `self`.
   1789     fn add_all_targets(&mut self) -> Result<(), ArgsErr> {
   1790         match *self {
   1791             Self::Check(ref mut check_targets) => match **check_targets {
   1792                 CheckClippyTargets::Default => {
   1793                     **check_targets = CheckClippyTargets::All;
   1794                     Ok(())
   1795                 }
   1796                 CheckClippyTargets::All => {
   1797                     Err(ArgsErr::DuplicateOption(match ALL_TARGETS.parse() {
   1798                         Ok(arg) => arg,
   1799                         Err(e) => match e {},
   1800                     }))
   1801                 }
   1802                 CheckClippyTargets::Targets(_) => Err(ArgsErr::AllTargets),
   1803             },
   1804             Self::Clippy(&mut (ref mut clippy_targets, _)) => match *clippy_targets {
   1805                 CheckClippyTargets::Default => {
   1806                     *clippy_targets = CheckClippyTargets::All;
   1807                     Ok(())
   1808                 }
   1809                 CheckClippyTargets::All => {
   1810                     Err(ArgsErr::DuplicateOption(match ALL_TARGETS.parse() {
   1811                         Ok(arg) => arg,
   1812                         Err(e) => match e {},
   1813                     }))
   1814                 }
   1815                 CheckClippyTargets::Targets(_) => Err(ArgsErr::AllTargets),
   1816             },
   1817             Self::Test(&mut (ref mut test_targets, _)) => match *test_targets {
   1818                 TestTargets::Default => {
   1819                     *test_targets = TestTargets::All;
   1820                     Ok(())
   1821                 }
   1822                 TestTargets::All => Err(ArgsErr::DuplicateOption(match ALL_TARGETS.parse() {
   1823                     Ok(arg) => arg,
   1824                     Err(e) => match e {},
   1825                 })),
   1826                 TestTargets::Targets(_) | TestTargets::Doc => Err(ArgsErr::AllTargets),
   1827             },
   1828         }
   1829     }
   1830     /// Adds the `target` to `self`.
   1831     fn add_target(&mut self, target: Target) -> Result<(), ArgsErr> {
   1832         match *self {
   1833             Self::Check(ref mut check_targets) => match **check_targets {
   1834                 CheckClippyTargets::Default => {
   1835                     **check_targets = CheckClippyTargets::Targets(Targets::new(target));
   1836                     Ok(())
   1837                 }
   1838                 CheckClippyTargets::All => Err(ArgsErr::AllTargets),
   1839                 CheckClippyTargets::Targets(ref mut targets) => {
   1840                     if targets.add(target) {
   1841                         Ok(())
   1842                     } else {
   1843                         Err(ArgsErr::DuplicateOption(target.to_os_string()))
   1844                     }
   1845                 }
   1846             },
   1847             Self::Clippy(&mut (ref mut clippy_targets, _)) => match *clippy_targets {
   1848                 CheckClippyTargets::Default => {
   1849                     *clippy_targets = CheckClippyTargets::Targets(Targets::new(target));
   1850                     Ok(())
   1851                 }
   1852                 CheckClippyTargets::All => Err(ArgsErr::AllTargets),
   1853                 CheckClippyTargets::Targets(ref mut targets) => {
   1854                     if targets.add(target) {
   1855                         Ok(())
   1856                     } else {
   1857                         Err(ArgsErr::DuplicateOption(target.to_os_string()))
   1858                     }
   1859                 }
   1860             },
   1861             Self::Test(&mut (ref mut test_targets, _)) => match *test_targets {
   1862                 TestTargets::Default => {
   1863                     *test_targets = TestTargets::Targets(Targets::new(target));
   1864                     Ok(())
   1865                 }
   1866                 TestTargets::All => Err(ArgsErr::AllTargets),
   1867                 TestTargets::Targets(ref mut targets) => {
   1868                     if targets.add(target) {
   1869                         Ok(())
   1870                     } else {
   1871                         Err(ArgsErr::DuplicateOption(target.to_os_string()))
   1872                     }
   1873                 }
   1874                 TestTargets::Doc => Err(ArgsErr::Doc),
   1875             },
   1876         }
   1877     }
   1878 }
   1879 /// Helper to store options from the command line.
   1880 #[expect(
   1881     clippy::struct_excessive_bools,
   1882     reason = "used exclusively in the recursive function MetaCmd::from_args::extract_options"
   1883 )]
   1884 #[derive(Default)]
   1885 struct ArgOpts {
   1886     /// `--allow-implied-features`.
   1887     allow_implied_features: bool,
   1888     /// `--cargo-home` along with the path.
   1889     cargo_home: Option<PathBuf>,
   1890     /// `--cargo-path` along with the path.
   1891     cargo_path: Option<PathBuf>,
   1892     /// `--color`.
   1893     color: bool,
   1894     /// `--default-toolchain`.
   1895     default_toolchain: bool,
   1896     /// `--dir` along with the path.
   1897     dir: Option<PathBuf>,
   1898     /// `--ignore-compile-errors`.
   1899     ignore_compile_errors: bool,
   1900     /// `--ignore-msrv`.
   1901     ignore_msrv: bool,
   1902     /// `--ignore-features` along with the features to ignore.
   1903     ignore_features: Vec<String>,
   1904     /// `--progress`.
   1905     progress: bool,
   1906     /// `--rustup-home` along with the path.
   1907     rustup_home: Option<PathBuf>,
   1908     /// `--skip-msrv`.
   1909     skip_msrv: bool,
   1910     /// `--summary`.
   1911     summary: bool,
   1912 }
   1913 /// Returns `"cargo"`.
   1914 fn cargo_path() -> PathBuf {
   1915     CARGO.to_owned().into()
   1916 }
   1917 impl From<ArgOpts> for Opts {
   1918     fn from(value: ArgOpts) -> Self {
   1919         Self {
   1920             exec_dir: value.dir,
   1921             rustup_home: value.rustup_home,
   1922             cargo_path: value.cargo_path.unwrap_or_else(cargo_path),
   1923             cargo_home: value.cargo_home,
   1924             color: value.color,
   1925             default_toolchain: value.default_toolchain,
   1926             allow_implied_features: value.allow_implied_features,
   1927             ignore_compile_errors: value.ignore_compile_errors,
   1928             ignore_msrv: value.ignore_msrv,
   1929             progress: value.progress,
   1930             skip_msrv: value.skip_msrv,
   1931             summary: value.summary,
   1932             ignore_features: value.ignore_features,
   1933         }
   1934     }
   1935 }
   1936 /// `ci-cargo` command to run.
   1937 #[cfg_attr(test, derive(Debug, PartialEq))]
   1938 pub(crate) enum MetaCmd {
   1939     /// Run the `cargo` command(s).
   1940     Cargo(Cmd, Opts),
   1941     /// Write help message to stdout.
   1942     Help,
   1943     /// Write version info to stdout.
   1944     Version,
   1945 }
   1946 impl MetaCmd {
   1947     /// Extracts options from `args`.
   1948     ///
   1949     /// This must only be called from [`Self::from_args`]. `cmd` is the current command command-specific
   1950     /// options apply to.
   1951     ///
   1952     /// Returns the next `CargoCmd`.
   1953     #[expect(unsafe_code, reason = "comment justifies correctness")]
   1954     #[expect(
   1955         clippy::arithmetic_side_effects,
   1956         reason = "comment justifies correctness"
   1957     )]
   1958     #[expect(
   1959         clippy::too_many_lines,
   1960         reason = "expected since we need to extract all the passed options"
   1961     )]
   1962     fn extract_options<T: Iterator<Item = OsString>>(
   1963         cmd: &mut CargoCmd<'_>,
   1964         opts: &mut ArgOpts,
   1965         mut args: T,
   1966     ) -> Result<Option<OsString>, ArgsErr> {
   1967         while let Some(arg) = args.next() {
   1968             if let Some(arg_str) = arg.to_str() {
   1969                 match arg_str {
   1970                     ALL_TARGETS => cmd.add_all_targets(),
   1971                     ALLOW_IMPLIED_FEATURES => {
   1972                         if opts.allow_implied_features {
   1973                             Err(ArgsErr::DuplicateOption(arg))
   1974                         } else {
   1975                             opts.allow_implied_features = true;
   1976                             Ok(())
   1977                         }
   1978                     }
   1979                     BENCHES => cmd.add_target(Target::Benches),
   1980                     BINS => cmd.add_target(Target::Bins),
   1981                     CARGO_HOME => args.next().map_or(Err(ArgsErr::MissingCargoHome), |path| {
   1982                         opts.cargo_home
   1983                             .replace(path.into())
   1984                             .map_or(Ok(()), |_| Err(ArgsErr::DuplicateOption(arg)))
   1985                     }),
   1986                     CARGO_PATH => args.next().map_or(Err(ArgsErr::MissingCargoPath), |p| {
   1987                         // This won't overflow since `p.len() + 6 < usize::MAX` since
   1988                         // `p.len() <= isize::MAX`, `usize::MAX >= u16::MAX`, and
   1989                         // `i16::MAX + 6 < u16::MAX`.
   1990                         let mut path = PathBuf::with_capacity(CARGO.len() + 1 + p.len());
   1991                         path.push(p);
   1992                         path.push(CARGO);
   1993                         opts.cargo_path
   1994                             .replace(path)
   1995                             .map_or(Ok(()), |_| Err(ArgsErr::DuplicateOption(arg)))
   1996                     }),
   1997                     COLOR => {
   1998                         if opts.color {
   1999                             Err(ArgsErr::DuplicateOption(arg))
   2000                         } else {
   2001                             opts.color = true;
   2002                             Ok(())
   2003                         }
   2004                     }
   2005                     DEFAULT_TOOLCHAIN => {
   2006                         if opts.default_toolchain {
   2007                             Err(ArgsErr::DuplicateOption(arg))
   2008                         } else {
   2009                             opts.default_toolchain = true;
   2010                             Ok(())
   2011                         }
   2012                     }
   2013                     DENY_WARNINGS => match *cmd {
   2014                         CargoCmd::Clippy(&mut (_, ref mut deny_warnings)) => {
   2015                             if *deny_warnings {
   2016                                 Err(ArgsErr::DuplicateOption(arg))
   2017                             } else {
   2018                                 *deny_warnings = true;
   2019                                 Ok(())
   2020                             }
   2021                         }
   2022                         CargoCmd::Check(_) | CargoCmd::Test(_) => Err(ArgsErr::UnknownArg(arg)),
   2023                     },
   2024                     DIR => args.next().map_or(Err(ArgsErr::MissingDirPath), |path| {
   2025                         opts.dir
   2026                             .replace(path.into())
   2027                             .map_or(Ok(()), |_| Err(ArgsErr::DuplicateOption(arg)))
   2028                     }),
   2029                     DOC => match *cmd {
   2030                         CargoCmd::Test(&mut (ref mut test_targets, _)) => match *test_targets {
   2031                             TestTargets::Default => {
   2032                                 *test_targets = TestTargets::Doc;
   2033                                 Ok(())
   2034                             }
   2035                             TestTargets::All | TestTargets::Targets(_) => Err(ArgsErr::Doc),
   2036                             TestTargets::Doc => Err(ArgsErr::DuplicateOption(arg)),
   2037                         },
   2038                         CargoCmd::Check(_) | CargoCmd::Clippy(_) => Err(ArgsErr::UnknownArg(arg)),
   2039                     },
   2040                     EXAMPLES => cmd.add_target(Target::Examples),
   2041                     IGNORE_COMPILE_ERRORS => {
   2042                         if opts.ignore_compile_errors {
   2043                             Err(ArgsErr::DuplicateOption(arg))
   2044                         } else {
   2045                             opts.ignore_compile_errors = true;
   2046                             Ok(())
   2047                         }
   2048                     }
   2049                     IGNORE_FEATURES => {
   2050                         if opts.ignore_features.is_empty() {
   2051                             args.next()
   2052                                 .map_or(Err(ArgsErr::MissingIgnoredFeatures), |feats_os| {
   2053                                     if let Some(feats) = feats_os.to_str() {
   2054                                         feats
   2055                                             .as_bytes()
   2056                                             .split(|b| *b == b',')
   2057                                             .try_fold((), |(), feat| {
   2058                                                 if opts
   2059                                                     .ignore_features
   2060                                                     .iter()
   2061                                                     .any(|f| f.as_bytes() == feat)
   2062                                                 {
   2063                                                     Err(())
   2064                                                 } else {
   2065                                                     let utf8 = feat.to_owned();
   2066                                                     // SAFETY:
   2067                                                     // `feats` is a valid `str` and was split by
   2068                                                     // a single UTF-8 code unit; thus `utf8` is also
   2069                                                     // valid UTF-8.
   2070                                                     opts.ignore_features.push(unsafe {
   2071                                                         String::from_utf8_unchecked(utf8)
   2072                                                     });
   2073                                                     Ok(())
   2074                                                 }
   2075                                             })
   2076                                             .map_err(|()| {
   2077                                                 ArgsErr::DuplicateIgnoredFeatures(feats_os)
   2078                                             })
   2079                                     } else {
   2080                                         Err(ArgsErr::UnknownArg(feats_os))
   2081                                     }
   2082                                 })
   2083                         } else {
   2084                             Err(ArgsErr::DuplicateOption(arg))
   2085                         }
   2086                     }
   2087                     IGNORE_MSRV => {
   2088                         if opts.ignore_msrv {
   2089                             Err(ArgsErr::DuplicateOption(arg))
   2090                         } else {
   2091                             opts.ignore_msrv = true;
   2092                             Ok(())
   2093                         }
   2094                     }
   2095                     IGNORED => match *cmd {
   2096                         CargoCmd::Test(&mut (_, ref mut ignored)) => match *ignored {
   2097                             Ignored::None => {
   2098                                 *ignored = Ignored::Only;
   2099                                 Ok(())
   2100                             }
   2101                             Ignored::Only => Err(ArgsErr::DuplicateOption(arg)),
   2102                             Ignored::Include => Err(ArgsErr::IgnoredIncludeIgnored),
   2103                         },
   2104                         CargoCmd::Check(_) | CargoCmd::Clippy(_) => Err(ArgsErr::UnknownArg(arg)),
   2105                     },
   2106                     INCLUDE_IGNORED => match *cmd {
   2107                         CargoCmd::Test(&mut (_, ref mut ignored)) => match *ignored {
   2108                             Ignored::None => {
   2109                                 *ignored = Ignored::Include;
   2110                                 Ok(())
   2111                             }
   2112                             Ignored::Only => Err(ArgsErr::IgnoredIncludeIgnored),
   2113                             Ignored::Include => Err(ArgsErr::DuplicateOption(arg)),
   2114                         },
   2115                         CargoCmd::Check(_) | CargoCmd::Clippy(_) => Err(ArgsErr::UnknownArg(arg)),
   2116                     },
   2117                     LIB => cmd.add_target(Target::Lib),
   2118                     PROGRESS => {
   2119                         if opts.progress {
   2120                             Err(ArgsErr::DuplicateOption(arg))
   2121                         } else {
   2122                             opts.progress = true;
   2123                             Ok(())
   2124                         }
   2125                     }
   2126                     RUSTUP_HOME => args.next().map_or(Err(ArgsErr::MissingRustupHome), |path| {
   2127                         opts.rustup_home
   2128                             .replace(path.into())
   2129                             .map_or(Ok(()), |_| Err(ArgsErr::DuplicateOption(arg)))
   2130                     }),
   2131                     SKIP_MSRV => {
   2132                         if opts.skip_msrv {
   2133                             Err(ArgsErr::DuplicateOption(arg))
   2134                         } else {
   2135                             opts.skip_msrv = true;
   2136                             Ok(())
   2137                         }
   2138                     }
   2139                     SUMMARY => {
   2140                         if opts.summary {
   2141                             Err(ArgsErr::DuplicateOption(arg))
   2142                         } else {
   2143                             opts.summary = true;
   2144                             Ok(())
   2145                         }
   2146                     }
   2147                     TESTS => cmd.add_target(Target::Tests),
   2148                     _ => return Ok(Some(arg)),
   2149                 }
   2150             } else {
   2151                 Err(ArgsErr::UnknownArg(arg))
   2152             }?;
   2153         }
   2154         Ok(None)
   2155     }
   2156     /// Returns data we need by reading the supplied CLI arguments.
   2157     #[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
   2158     pub(crate) fn from_args<T: Iterator<Item = OsString>>(mut args: T) -> Result<Self, ArgsErr> {
   2159         args.next().ok_or(ArgsErr::NoArgs).and_then(|_| {
   2160             args.next().ok_or(ArgsErr::NoCommand).and_then(|fst_cmd| {
   2161                 if let Some(fst_cmd_str) = fst_cmd.to_str() {
   2162                     let mut opts = ArgOpts::default();
   2163                     let mut check_targets = None;
   2164                     let mut clippy_info: Option<(CheckClippyTargets, bool)> = None;
   2165                     let mut test_info: Option<(TestTargets, Ignored)> = None;
   2166                     let mut cargo_cmd = match fst_cmd_str {
   2167                         CHECK => CargoCmd::Check(check_targets.insert(CheckClippyTargets::Default)),
   2168                         CLIPPY => CargoCmd::Clippy(
   2169                             clippy_info.insert((CheckClippyTargets::Default, false)),
   2170                         ),
   2171                         HELP => {
   2172                             return args
   2173                                 .next()
   2174                                 .map_or_else(|| Ok(Self::Help), |_| Err(ArgsErr::HelpWithArgs));
   2175                         }
   2176                         TEST => {
   2177                             CargoCmd::Test(test_info.insert((TestTargets::Default, Ignored::None)))
   2178                         }
   2179                         VERSION => {
   2180                             return args.next().map_or_else(
   2181                                 || Ok(Self::Version),
   2182                                 |_| Err(ArgsErr::VersionWithArgs),
   2183                             );
   2184                         }
   2185                         _ => return Err(ArgsErr::NoCommand),
   2186                     };
   2187                     while let Some(arg) =
   2188                         Self::extract_options(&mut cargo_cmd, &mut opts, &mut args)?
   2189                     {
   2190                         match arg.to_str().unwrap_or_else(|| {
   2191                             unreachable!("there is a bug in args::MetaCmd::extract_options")
   2192                         }) {
   2193                             CHECK => {
   2194                                 if check_targets.is_some() {
   2195                                     return Err(ArgsErr::DuplicateOption(arg));
   2196                                 }
   2197                                 cargo_cmd = CargoCmd::Check(
   2198                                     check_targets.insert(CheckClippyTargets::Default),
   2199                                 );
   2200                             }
   2201                             CLIPPY => {
   2202                                 if clippy_info.is_some() {
   2203                                     return Err(ArgsErr::DuplicateOption(arg));
   2204                                 }
   2205                                 cargo_cmd = CargoCmd::Clippy(
   2206                                     clippy_info.insert((CheckClippyTargets::Default, false)),
   2207                                 );
   2208                             }
   2209                             TEST => {
   2210                                 if test_info.is_some() {
   2211                                     return Err(ArgsErr::DuplicateOption(arg));
   2212                                 }
   2213                                 cargo_cmd = CargoCmd::Test(
   2214                                     test_info.insert((TestTargets::Default, Ignored::None)),
   2215                                 );
   2216                             }
   2217                             _ => return Err(ArgsErr::UnknownArg(arg)),
   2218                         }
   2219                     }
   2220                     if let Some(check) = check_targets {
   2221                         Ok(Self::Cargo(
   2222                             clippy_info.map_or_else(
   2223                                 || {
   2224                                     test_info.map_or(Cmd::Check(check), |test| {
   2225                                         Cmd::CheckTest(check, test.0, test.1)
   2226                                     })
   2227                                 },
   2228                                 |clippy| {
   2229                                     test_info.map_or(
   2230                                         Cmd::CheckClippy(check, clippy.0, clippy.1),
   2231                                         |test| {
   2232                                             Cmd::CheckClippyTest(
   2233                                                 check, clippy.0, clippy.1, test.0, test.1,
   2234                                             )
   2235                                         },
   2236                                     )
   2237                                 },
   2238                             ),
   2239                             opts.into(),
   2240                         ))
   2241                     } else if let Some(clippy) = clippy_info {
   2242                         Ok(Self::Cargo(
   2243                             test_info.map_or(Cmd::Clippy(clippy.0, clippy.1), |test| {
   2244                                 Cmd::ClippyTest(clippy.0, clippy.1, test.0, test.1)
   2245                             }),
   2246                             opts.into(),
   2247                         ))
   2248                     } else if let Some(test) = test_info {
   2249                         Ok(Self::Cargo(Cmd::Test(test.0, test.1), opts.into()))
   2250                     } else {
   2251                         Err(ArgsErr::NoCommand)
   2252                     }
   2253                 } else {
   2254                     Err(ArgsErr::UnknownArg(fst_cmd))
   2255                 }
   2256             })
   2257         })
   2258     }
   2259 }
   2260 #[cfg(test)]
   2261 mod tests {
   2262     use super::{
   2263         ArgsErr, CheckClippyTargets, Cmd, Ignored, MetaCmd, NonZeroUsizePlus1, Opts, OsString,
   2264         PathBuf, Target, Targets, TestTargets,
   2265     };
   2266     use core::iter;
   2267     #[cfg(unix)]
   2268     use std::os::unix::ffi::OsStringExt as _;
   2269     #[expect(
   2270         clippy::cognitive_complexity,
   2271         clippy::too_many_lines,
   2272         reason = "want to test for a lot of things"
   2273     )]
   2274     #[test]
   2275     fn arg_parsing() {
   2276         assert_eq!(MetaCmd::from_args(iter::empty()), Err(ArgsErr::NoArgs));
   2277         assert_eq!(
   2278             MetaCmd::from_args(iter::once(OsString::new())),
   2279             Err(ArgsErr::NoCommand)
   2280         );
   2281         assert_eq!(
   2282             MetaCmd::from_args([OsString::new(), OsString::new()].into_iter()),
   2283             Err(ArgsErr::NoCommand)
   2284         );
   2285         // Invalid UTF-8 errors gracefully.
   2286         #[cfg(unix)]
   2287         assert_eq!(
   2288             MetaCmd::from_args([OsString::new(), OsString::from_vec(vec![255])].into_iter()),
   2289             Err(ArgsErr::UnknownArg(OsString::from_vec(vec![255])))
   2290         );
   2291         // Whitespace is not ignored.
   2292         assert_eq!(
   2293             MetaCmd::from_args([OsString::new(), " clippy".to_owned().into()].into_iter()),
   2294             Err(ArgsErr::NoCommand)
   2295         );
   2296         // We parse in a case-sensitive way.
   2297         assert_eq!(
   2298             MetaCmd::from_args([OsString::new(), "Clippy".to_owned().into()].into_iter()),
   2299             Err(ArgsErr::NoCommand)
   2300         );
   2301         // We require options to be after the command (if one was passed).
   2302         assert_eq!(
   2303             MetaCmd::from_args(
   2304                 [
   2305                     OsString::new(),
   2306                     "--summary".to_owned().into(),
   2307                     "clippy".to_owned().into()
   2308                 ]
   2309                 .into_iter()
   2310             ),
   2311             Err(ArgsErr::NoCommand)
   2312         );
   2313         assert_eq!(
   2314             MetaCmd::from_args(
   2315                 [
   2316                     OsString::new(),
   2317                     "help".to_owned().into(),
   2318                     "--summary".to_owned().into()
   2319                 ]
   2320                 .into_iter()
   2321             ),
   2322             Err(ArgsErr::HelpWithArgs)
   2323         );
   2324         assert_eq!(
   2325             MetaCmd::from_args(
   2326                 [
   2327                     OsString::new(),
   2328                     "version".to_owned().into(),
   2329                     "foo".to_owned().into()
   2330                 ]
   2331                 .into_iter()
   2332             ),
   2333             Err(ArgsErr::VersionWithArgs)
   2334         );
   2335         assert_eq!(
   2336             MetaCmd::from_args(
   2337                 [
   2338                     OsString::new(),
   2339                     "check".to_owned().into(),
   2340                     "--cargo-path".to_owned().into()
   2341                 ]
   2342                 .into_iter()
   2343             ),
   2344             Err(ArgsErr::MissingCargoPath)
   2345         );
   2346         assert_eq!(
   2347             MetaCmd::from_args(
   2348                 [
   2349                     OsString::new(),
   2350                     "check".to_owned().into(),
   2351                     "--cargo-home".to_owned().into()
   2352                 ]
   2353                 .into_iter()
   2354             ),
   2355             Err(ArgsErr::MissingCargoHome)
   2356         );
   2357         assert_eq!(
   2358             MetaCmd::from_args(
   2359                 [
   2360                     OsString::new(),
   2361                     "check".to_owned().into(),
   2362                     "--rustup-home".to_owned().into()
   2363                 ]
   2364                 .into_iter()
   2365             ),
   2366             Err(ArgsErr::MissingRustupHome)
   2367         );
   2368         assert_eq!(
   2369             MetaCmd::from_args(
   2370                 [
   2371                     OsString::new(),
   2372                     "test".to_owned().into(),
   2373                     "--deny-warnings".to_owned().into()
   2374                 ]
   2375                 .into_iter()
   2376             ),
   2377             Err(ArgsErr::UnknownArg("--deny-warnings".to_owned().into()))
   2378         );
   2379         assert_eq!(
   2380             MetaCmd::from_args(
   2381                 [
   2382                     OsString::new(),
   2383                     "check".to_owned().into(),
   2384                     "--deny-warnings".to_owned().into()
   2385                 ]
   2386                 .into_iter()
   2387             ),
   2388             Err(ArgsErr::UnknownArg("--deny-warnings".to_owned().into()))
   2389         );
   2390         assert_eq!(
   2391             MetaCmd::from_args(
   2392                 [
   2393                     OsString::new(),
   2394                     "clippy".to_owned().into(),
   2395                     "--ignored".to_owned().into()
   2396                 ]
   2397                 .into_iter()
   2398             ),
   2399             Err(ArgsErr::UnknownArg("--ignored".to_owned().into()))
   2400         );
   2401         assert_eq!(
   2402             MetaCmd::from_args(
   2403                 [
   2404                     OsString::new(),
   2405                     "check".to_owned().into(),
   2406                     "--ignored".to_owned().into()
   2407                 ]
   2408                 .into_iter()
   2409             ),
   2410             Err(ArgsErr::UnknownArg("--ignored".to_owned().into()))
   2411         );
   2412         assert_eq!(
   2413             MetaCmd::from_args(
   2414                 [
   2415                     OsString::new(),
   2416                     "clippy".to_owned().into(),
   2417                     "--include-ignored".to_owned().into()
   2418                 ]
   2419                 .into_iter()
   2420             ),
   2421             Err(ArgsErr::UnknownArg("--include-ignored".to_owned().into()))
   2422         );
   2423         assert_eq!(
   2424             MetaCmd::from_args(
   2425                 [
   2426                     OsString::new(),
   2427                     "check".to_owned().into(),
   2428                     "--include-ignored".to_owned().into()
   2429                 ]
   2430                 .into_iter()
   2431             ),
   2432             Err(ArgsErr::UnknownArg("--include-ignored".to_owned().into()))
   2433         );
   2434         assert_eq!(
   2435             MetaCmd::from_args(
   2436                 [
   2437                     OsString::new(),
   2438                     "test".to_owned().into(),
   2439                     "--ignored".to_owned().into(),
   2440                     "--include-ignored".to_owned().into()
   2441                 ]
   2442                 .into_iter()
   2443             ),
   2444             Err(ArgsErr::IgnoredIncludeIgnored)
   2445         );
   2446         assert_eq!(
   2447             MetaCmd::from_args(
   2448                 [
   2449                     OsString::new(),
   2450                     "test".to_owned().into(),
   2451                     "--ignore-features".to_owned().into(),
   2452                 ]
   2453                 .into_iter()
   2454             ),
   2455             Err(ArgsErr::MissingIgnoredFeatures)
   2456         );
   2457         assert_eq!(
   2458             MetaCmd::from_args(
   2459                 [
   2460                     OsString::new(),
   2461                     "clippy".to_owned().into(),
   2462                     "--ignore-features".to_owned().into(),
   2463                     ",".to_owned().into(),
   2464                 ]
   2465                 .into_iter()
   2466             ),
   2467             Err(ArgsErr::DuplicateIgnoredFeatures(",".to_owned().into()))
   2468         );
   2469         assert_eq!(
   2470             MetaCmd::from_args(
   2471                 [
   2472                     OsString::new(),
   2473                     "check".to_owned().into(),
   2474                     "--ignore-features".to_owned().into(),
   2475                     "a,,a".to_owned().into(),
   2476                 ]
   2477                 .into_iter()
   2478             ),
   2479             Err(ArgsErr::DuplicateIgnoredFeatures("a,,a".to_owned().into()))
   2480         );
   2481         assert_eq!(
   2482             MetaCmd::from_args(
   2483                 [
   2484                     OsString::new(),
   2485                     "check".to_owned().into(),
   2486                     "--ignore-features".to_owned().into(),
   2487                     ",a,b,".to_owned().into(),
   2488                 ]
   2489                 .into_iter()
   2490             ),
   2491             Err(ArgsErr::DuplicateIgnoredFeatures(",a,b,".to_owned().into()))
   2492         );
   2493         assert_eq!(
   2494             MetaCmd::from_args(
   2495                 [
   2496                     OsString::new(),
   2497                     "clippy".to_owned().into(),
   2498                     "--ignore-features".to_owned().into(),
   2499                     ",a,,b".to_owned().into(),
   2500                 ]
   2501                 .into_iter()
   2502             ),
   2503             Err(ArgsErr::DuplicateIgnoredFeatures(",a,,b".to_owned().into()))
   2504         );
   2505         assert_eq!(
   2506             MetaCmd::from_args(
   2507                 [
   2508                     OsString::new(),
   2509                     "clippy".to_owned().into(),
   2510                     "--ignore-features".to_owned().into(),
   2511                     "a,b,,".to_owned().into(),
   2512                 ]
   2513                 .into_iter()
   2514             ),
   2515             Err(ArgsErr::DuplicateIgnoredFeatures("a,b,,".to_owned().into()))
   2516         );
   2517         // `--all-targets` can't be combined with other targets.
   2518         assert_eq!(
   2519             MetaCmd::from_args(
   2520                 [
   2521                     OsString::new(),
   2522                     "clippy".to_owned().into(),
   2523                     "--lib".to_owned().into(),
   2524                     "--all-targets".to_owned().into(),
   2525                 ]
   2526                 .into_iter()
   2527             ),
   2528             Err(ArgsErr::AllTargets)
   2529         );
   2530         assert_eq!(
   2531             MetaCmd::from_args(
   2532                 [
   2533                     OsString::new(),
   2534                     "check".to_owned().into(),
   2535                     "--lib".to_owned().into(),
   2536                     "--all-targets".to_owned().into(),
   2537                 ]
   2538                 .into_iter()
   2539             ),
   2540             Err(ArgsErr::AllTargets)
   2541         );
   2542         assert_eq!(
   2543             MetaCmd::from_args(
   2544                 [
   2545                     OsString::new(),
   2546                     "check".to_owned().into(),
   2547                     "--all-targets".to_owned().into(),
   2548                     "test".to_owned().into(),
   2549                     "--doc".to_owned().into(),
   2550                     "--all-targets".to_owned().into(),
   2551                 ]
   2552                 .into_iter()
   2553             ),
   2554             Err(ArgsErr::AllTargets)
   2555         );
   2556         // `--doc` can't be combined with other targets.
   2557         assert_eq!(
   2558             MetaCmd::from_args(
   2559                 [
   2560                     OsString::new(),
   2561                     "test".to_owned().into(),
   2562                     "--all-targets".to_owned().into(),
   2563                     "--doc".to_owned().into(),
   2564                 ]
   2565                 .into_iter()
   2566             ),
   2567             Err(ArgsErr::Doc)
   2568         );
   2569         assert_eq!(
   2570             MetaCmd::from_args(
   2571                 [
   2572                     OsString::new(),
   2573                     "test".to_owned().into(),
   2574                     "--doc".to_owned().into(),
   2575                     "--lib".to_owned().into(),
   2576                 ]
   2577                 .into_iter()
   2578             ),
   2579             Err(ArgsErr::Doc)
   2580         );
   2581         assert_eq!(
   2582             MetaCmd::from_args(
   2583                 [
   2584                     OsString::new(),
   2585                     "clippy".to_owned().into(),
   2586                     "--doc".to_owned().into(),
   2587                 ]
   2588                 .into_iter()
   2589             ),
   2590             Err(ArgsErr::UnknownArg("--doc".to_owned().into()))
   2591         );
   2592         assert_eq!(
   2593             MetaCmd::from_args(
   2594                 [
   2595                     OsString::new(),
   2596                     "check".to_owned().into(),
   2597                     "--color".to_owned().into(),
   2598                     "clippy".to_owned().into(),
   2599                     "--color".to_owned().into(),
   2600                 ]
   2601                 .into_iter()
   2602             ),
   2603             Err(ArgsErr::DuplicateOption("--color".to_owned().into()))
   2604         );
   2605         assert_eq!(
   2606             MetaCmd::from_args(
   2607                 [
   2608                     OsString::new(),
   2609                     "test".to_owned().into(),
   2610                     "--ignored".to_owned().into(),
   2611                     "--ignored".to_owned().into(),
   2612                 ]
   2613                 .into_iter()
   2614             ),
   2615             Err(ArgsErr::DuplicateOption("--ignored".to_owned().into()))
   2616         );
   2617         assert_eq!(
   2618             MetaCmd::from_args(
   2619                 [
   2620                     OsString::new(),
   2621                     "clippy".to_owned().into(),
   2622                     "--all-targets".to_owned().into(),
   2623                     "--allow-implied-features".to_owned().into(),
   2624                     "--cargo-home".to_owned().into(),
   2625                     "--ignored".to_owned().into(),
   2626                     "--cargo-path".to_owned().into(),
   2627                     "cargo".to_owned().into(),
   2628                     "--color".to_owned().into(),
   2629                     "--default-toolchain".to_owned().into(),
   2630                     "--deny-warnings".to_owned().into(),
   2631                     "--dir".to_owned().into(),
   2632                     OsString::new(),
   2633                     "--ignore-compile-errors".to_owned().into(),
   2634                     "--ignore-features".to_owned().into(),
   2635                     ",a".to_owned().into(),
   2636                     "--ignore-msrv".to_owned().into(),
   2637                     "--rustup-home".to_owned().into(),
   2638                     "a".to_owned().into(),
   2639                     "--progress".to_owned().into(),
   2640                     "--skip-msrv".to_owned().into(),
   2641                     "--summary".to_owned().into(),
   2642                 ]
   2643                 .into_iter()
   2644             ),
   2645             Ok(MetaCmd::Cargo(
   2646                 Cmd::Clippy(CheckClippyTargets::All, true,),
   2647                 Opts {
   2648                     exec_dir: Some(PathBuf::new()),
   2649                     rustup_home: Some("a".to_owned().into()),
   2650                     cargo_home: Some("--ignored".to_owned().into()),
   2651                     cargo_path: "cargo/cargo".to_owned().into(),
   2652                     color: true,
   2653                     default_toolchain: true,
   2654                     allow_implied_features: true,
   2655                     ignore_compile_errors: true,
   2656                     ignore_msrv: true,
   2657                     progress: true,
   2658                     skip_msrv: true,
   2659                     summary: true,
   2660                     ignore_features: vec![String::new(), "a".to_owned()],
   2661                 }
   2662             ))
   2663         );
   2664         assert_eq!(
   2665             MetaCmd::from_args(
   2666                 [
   2667                     OsString::new(),
   2668                     "clippy".to_owned().into(),
   2669                     "--all-targets".to_owned().into(),
   2670                     "--deny-warnings".to_owned().into(),
   2671                 ]
   2672                 .into_iter()
   2673             ),
   2674             Ok(MetaCmd::Cargo(
   2675                 Cmd::Clippy(CheckClippyTargets::All, true,),
   2676                 Opts {
   2677                     exec_dir: None,
   2678                     rustup_home: None,
   2679                     cargo_home: None,
   2680                     cargo_path: "cargo".to_owned().into(),
   2681                     color: false,
   2682                     default_toolchain: false,
   2683                     allow_implied_features: false,
   2684                     ignore_compile_errors: false,
   2685                     ignore_msrv: false,
   2686                     progress: false,
   2687                     skip_msrv: false,
   2688                     summary: false,
   2689                     ignore_features: Vec::new(),
   2690                 }
   2691             ))
   2692         );
   2693         assert_eq!(
   2694             MetaCmd::from_args(
   2695                 [
   2696                     OsString::new(),
   2697                     "test".to_owned().into(),
   2698                     "--allow-implied-features".to_owned().into(),
   2699                     "--cargo-home".to_owned().into(),
   2700                     "--ignored".to_owned().into(),
   2701                     "--cargo-path".to_owned().into(),
   2702                     "cargo".to_owned().into(),
   2703                     "--color".to_owned().into(),
   2704                     "--default-toolchain".to_owned().into(),
   2705                     "--dir".to_owned().into(),
   2706                     OsString::new(),
   2707                     "--ignore-compile-errors".to_owned().into(),
   2708                     "--ignore-features".to_owned().into(),
   2709                     OsString::new(),
   2710                     "--ignore-msrv".to_owned().into(),
   2711                     "--ignored".to_owned().into(),
   2712                     "--rustup-home".to_owned().into(),
   2713                     OsString::new(),
   2714                     "--progress".to_owned().into(),
   2715                     "--skip-msrv".to_owned().into(),
   2716                     "--summary".to_owned().into(),
   2717                 ]
   2718                 .into_iter()
   2719             ),
   2720             Ok(MetaCmd::Cargo(
   2721                 Cmd::Test(TestTargets::Default, Ignored::Only),
   2722                 Opts {
   2723                     exec_dir: Some(PathBuf::new()),
   2724                     rustup_home: Some(PathBuf::new()),
   2725                     cargo_home: Some("--ignored".to_owned().into()),
   2726                     cargo_path: "cargo/cargo".to_owned().into(),
   2727                     color: true,
   2728                     default_toolchain: true,
   2729                     allow_implied_features: true,
   2730                     ignore_compile_errors: true,
   2731                     ignore_msrv: true,
   2732                     progress: true,
   2733                     skip_msrv: true,
   2734                     summary: true,
   2735                     ignore_features: vec![String::new()],
   2736                 }
   2737             ))
   2738         );
   2739         assert_eq!(
   2740             MetaCmd::from_args([OsString::new(), "test".to_owned().into(),].into_iter()),
   2741             Ok(MetaCmd::Cargo(
   2742                 Cmd::Test(TestTargets::Default, Ignored::None),
   2743                 Opts {
   2744                     exec_dir: None,
   2745                     rustup_home: None,
   2746                     cargo_home: None,
   2747                     cargo_path: "cargo".to_owned().into(),
   2748                     color: false,
   2749                     default_toolchain: false,
   2750                     allow_implied_features: false,
   2751                     ignore_compile_errors: false,
   2752                     ignore_msrv: false,
   2753                     progress: false,
   2754                     skip_msrv: false,
   2755                     summary: false,
   2756                     ignore_features: Vec::new(),
   2757                 }
   2758             ))
   2759         );
   2760         assert_eq!(
   2761             MetaCmd::from_args(
   2762                 [
   2763                     OsString::new(),
   2764                     "test".to_owned().into(),
   2765                     "--include-ignored".to_owned().into()
   2766                 ]
   2767                 .into_iter()
   2768             ),
   2769             Ok(MetaCmd::Cargo(
   2770                 Cmd::Test(TestTargets::Default, Ignored::Include),
   2771                 Opts {
   2772                     exec_dir: None,
   2773                     rustup_home: None,
   2774                     cargo_home: None,
   2775                     cargo_path: "cargo".to_owned().into(),
   2776                     color: false,
   2777                     default_toolchain: false,
   2778                     allow_implied_features: false,
   2779                     ignore_compile_errors: false,
   2780                     ignore_msrv: false,
   2781                     progress: false,
   2782                     skip_msrv: false,
   2783                     summary: false,
   2784                     ignore_features: Vec::new(),
   2785                 }
   2786             ))
   2787         );
   2788         assert_eq!(
   2789             MetaCmd::from_args(
   2790                 [
   2791                     OsString::new(),
   2792                     "check".to_owned().into(),
   2793                     "--all-targets".to_owned().into(),
   2794                     "--allow-implied-features".to_owned().into(),
   2795                     "--cargo-home".to_owned().into(),
   2796                     "--ignored".to_owned().into(),
   2797                     "--cargo-path".to_owned().into(),
   2798                     "cargo".to_owned().into(),
   2799                     "--color".to_owned().into(),
   2800                     "--default-toolchain".to_owned().into(),
   2801                     "--dir".to_owned().into(),
   2802                     OsString::new(),
   2803                     "--ignore-compile-errors".to_owned().into(),
   2804                     "--ignore-features".to_owned().into(),
   2805                     "a,".to_owned().into(),
   2806                     "--ignore-msrv".to_owned().into(),
   2807                     "--rustup-home".to_owned().into(),
   2808                     OsString::new(),
   2809                     "--progress".to_owned().into(),
   2810                     "--skip-msrv".to_owned().into(),
   2811                     "--summary".to_owned().into(),
   2812                 ]
   2813                 .into_iter()
   2814             ),
   2815             Ok(MetaCmd::Cargo(
   2816                 Cmd::Check(CheckClippyTargets::All),
   2817                 Opts {
   2818                     exec_dir: Some(PathBuf::new()),
   2819                     rustup_home: Some(PathBuf::new()),
   2820                     cargo_home: Some("--ignored".to_owned().into()),
   2821                     cargo_path: "cargo/cargo".to_owned().into(),
   2822                     color: true,
   2823                     default_toolchain: true,
   2824                     allow_implied_features: true,
   2825                     ignore_compile_errors: true,
   2826                     ignore_msrv: true,
   2827                     progress: true,
   2828                     skip_msrv: true,
   2829                     summary: true,
   2830                     ignore_features: vec!["a".to_owned(), String::new()],
   2831                 }
   2832             ))
   2833         );
   2834         assert_eq!(
   2835             MetaCmd::from_args([OsString::new(), "check".to_owned().into(),].into_iter()),
   2836             Ok(MetaCmd::Cargo(
   2837                 Cmd::Check(CheckClippyTargets::Default),
   2838                 Opts {
   2839                     exec_dir: None,
   2840                     rustup_home: None,
   2841                     cargo_home: None,
   2842                     cargo_path: "cargo".to_owned().into(),
   2843                     color: false,
   2844                     default_toolchain: false,
   2845                     allow_implied_features: false,
   2846                     ignore_compile_errors: false,
   2847                     ignore_msrv: false,
   2848                     progress: false,
   2849                     skip_msrv: false,
   2850                     summary: false,
   2851                     ignore_features: Vec::new(),
   2852                 }
   2853             ))
   2854         );
   2855         assert_eq!(
   2856             MetaCmd::from_args(
   2857                 [
   2858                     OsString::new(),
   2859                     "check".to_owned().into(),
   2860                     "--ignore-features".to_owned().into(),
   2861                     "a,,b".to_owned().into(),
   2862                 ]
   2863                 .into_iter()
   2864             ),
   2865             Ok(MetaCmd::Cargo(
   2866                 Cmd::Check(CheckClippyTargets::Default),
   2867                 Opts {
   2868                     exec_dir: None,
   2869                     rustup_home: None,
   2870                     cargo_home: None,
   2871                     cargo_path: "cargo".to_owned().into(),
   2872                     color: false,
   2873                     default_toolchain: false,
   2874                     allow_implied_features: false,
   2875                     ignore_compile_errors: false,
   2876                     ignore_msrv: false,
   2877                     progress: false,
   2878                     skip_msrv: false,
   2879                     summary: false,
   2880                     ignore_features: vec!["a".to_owned(), String::new(), "b".to_owned()],
   2881                 }
   2882             ))
   2883         );
   2884         assert_eq!(
   2885             MetaCmd::from_args(
   2886                 [
   2887                     OsString::new(),
   2888                     "check".to_owned().into(),
   2889                     "--ignore-features".to_owned().into(),
   2890                     "a,b,".to_owned().into(),
   2891                 ]
   2892                 .into_iter()
   2893             ),
   2894             Ok(MetaCmd::Cargo(
   2895                 Cmd::Check(CheckClippyTargets::Default),
   2896                 Opts {
   2897                     exec_dir: None,
   2898                     rustup_home: None,
   2899                     cargo_home: None,
   2900                     cargo_path: "cargo".to_owned().into(),
   2901                     color: false,
   2902                     default_toolchain: false,
   2903                     allow_implied_features: false,
   2904                     ignore_compile_errors: false,
   2905                     ignore_msrv: false,
   2906                     progress: false,
   2907                     skip_msrv: false,
   2908                     summary: false,
   2909                     ignore_features: vec!["a".to_owned(), "b".to_owned(), String::new()],
   2910                 }
   2911             ))
   2912         );
   2913         assert_eq!(
   2914             MetaCmd::from_args(
   2915                 [
   2916                     OsString::new(),
   2917                     "check".to_owned().into(),
   2918                     "--ignore-features".to_owned().into(),
   2919                     "a,b".to_owned().into(),
   2920                 ]
   2921                 .into_iter()
   2922             ),
   2923             Ok(MetaCmd::Cargo(
   2924                 Cmd::Check(CheckClippyTargets::Default),
   2925                 Opts {
   2926                     exec_dir: None,
   2927                     rustup_home: None,
   2928                     cargo_home: None,
   2929                     cargo_path: "cargo".to_owned().into(),
   2930                     color: false,
   2931                     default_toolchain: false,
   2932                     allow_implied_features: false,
   2933                     ignore_compile_errors: false,
   2934                     ignore_msrv: false,
   2935                     progress: false,
   2936                     skip_msrv: false,
   2937                     summary: false,
   2938                     ignore_features: vec!["a".to_owned(), "b".to_owned()],
   2939                 }
   2940             ))
   2941         );
   2942         // No whitespace cleanup is done on the features.
   2943         assert_eq!(
   2944             MetaCmd::from_args(
   2945                 [
   2946                     OsString::new(),
   2947                     "check".to_owned().into(),
   2948                     "--ignore-features".to_owned().into(),
   2949                     "a , , b,  ".to_owned().into(),
   2950                 ]
   2951                 .into_iter()
   2952             ),
   2953             Ok(MetaCmd::Cargo(
   2954                 Cmd::Check(CheckClippyTargets::Default),
   2955                 Opts {
   2956                     exec_dir: None,
   2957                     rustup_home: None,
   2958                     cargo_home: None,
   2959                     cargo_path: "cargo".to_owned().into(),
   2960                     color: false,
   2961                     default_toolchain: false,
   2962                     allow_implied_features: false,
   2963                     ignore_compile_errors: false,
   2964                     ignore_msrv: false,
   2965                     progress: false,
   2966                     skip_msrv: false,
   2967                     summary: false,
   2968                     ignore_features: vec![
   2969                         "a ".to_owned(),
   2970                         " ".to_owned(),
   2971                         " b".to_owned(),
   2972                         "  ".to_owned()
   2973                     ],
   2974                 }
   2975             ))
   2976         );
   2977         assert_eq!(
   2978             MetaCmd::from_args([OsString::new(), "help".to_owned().into(),].into_iter()),
   2979             Ok(MetaCmd::Help)
   2980         );
   2981         assert_eq!(
   2982             MetaCmd::from_args([OsString::new(), "version".to_owned().into(),].into_iter()),
   2983             Ok(MetaCmd::Version)
   2984         );
   2985         let mut check_targets = Targets::new(Target::Examples);
   2986         assert!(check_targets.add(Target::Tests));
   2987         assert!(!check_targets.add(Target::Examples));
   2988         let mut test_targets = Targets::new(Target::Benches);
   2989         assert!(test_targets.add(Target::Bins));
   2990         assert!(!test_targets.add(Target::Bins));
   2991         assert!(test_targets.add(Target::Examples));
   2992         assert!(test_targets.add(Target::Lib));
   2993         assert!(test_targets.add(Target::Tests));
   2994         assert_eq!(
   2995             MetaCmd::from_args(
   2996                 [
   2997                     OsString::new(),
   2998                     "clippy".to_owned().into(),
   2999                     "--all-targets".to_owned().into(),
   3000                     "--allow-implied-features".to_owned().into(),
   3001                     "--cargo-home".to_owned().into(),
   3002                     "--ignored".to_owned().into(),
   3003                     "--cargo-path".to_owned().into(),
   3004                     "cargo".to_owned().into(),
   3005                     "--color".to_owned().into(),
   3006                     "--default-toolchain".to_owned().into(),
   3007                     "--deny-warnings".to_owned().into(),
   3008                     "--dir".to_owned().into(),
   3009                     OsString::new(),
   3010                     "--ignore-compile-errors".to_owned().into(),
   3011                     "--ignore-features".to_owned().into(),
   3012                     ",a".to_owned().into(),
   3013                     "--ignore-msrv".to_owned().into(),
   3014                     "--rustup-home".to_owned().into(),
   3015                     "a".to_owned().into(),
   3016                     "test".to_owned().into(),
   3017                     "--benches".to_owned().into(),
   3018                     "--bins".to_owned().into(),
   3019                     "--examples".to_owned().into(),
   3020                     "--include-ignored".to_owned().into(),
   3021                     "--lib".to_owned().into(),
   3022                     "--tests".to_owned().into(),
   3023                     "--progress".to_owned().into(),
   3024                     "--skip-msrv".to_owned().into(),
   3025                     "check".to_owned().into(),
   3026                     "--tests".to_owned().into(),
   3027                     "--examples".to_owned().into(),
   3028                     "--summary".to_owned().into(),
   3029                 ]
   3030                 .into_iter()
   3031             ),
   3032             Ok(MetaCmd::Cargo(
   3033                 Cmd::CheckClippyTest(
   3034                     CheckClippyTargets::Targets(check_targets),
   3035                     CheckClippyTargets::All,
   3036                     true,
   3037                     TestTargets::Targets(test_targets),
   3038                     Ignored::Include,
   3039                 ),
   3040                 Opts {
   3041                     exec_dir: Some(PathBuf::new()),
   3042                     rustup_home: Some("a".to_owned().into()),
   3043                     cargo_home: Some("--ignored".to_owned().into()),
   3044                     cargo_path: "cargo/cargo".to_owned().into(),
   3045                     color: true,
   3046                     default_toolchain: true,
   3047                     allow_implied_features: true,
   3048                     ignore_compile_errors: true,
   3049                     ignore_msrv: true,
   3050                     progress: true,
   3051                     skip_msrv: true,
   3052                     summary: true,
   3053                     ignore_features: vec![String::new(), "a".to_owned()],
   3054                 }
   3055             ))
   3056         );
   3057     }
   3058     #[test]
   3059     fn non_zero_usize_plus_1() {
   3060         #[cfg(target_pointer_width = "64")]
   3061         assert_eq!(NonZeroUsizePlus1(0).to_string(), "18446744073709551616");
   3062         #[cfg(target_pointer_width = "64")]
   3063         assert_eq!(
   3064             NonZeroUsizePlus1(usize::MAX).to_string(),
   3065             "18446744073709551615"
   3066         );
   3067         #[cfg(target_pointer_width = "32")]
   3068         assert_eq!(NonZeroUsizePlus1(0).to_string(), "4294967296");
   3069         #[cfg(target_pointer_width = "32")]
   3070         assert_eq!(NonZeroUsizePlus1(usize::MAX).to_string(), "4294967295");
   3071         #[cfg(target_pointer_width = "16")]
   3072         assert_eq!(NonZeroUsizePlus1(0).to_string(), "65536");
   3073         #[cfg(target_pointer_width = "16")]
   3074         assert_eq!(NonZeroUsizePlus1(usize::MAX).to_string(), "65535");
   3075         assert_eq!(NonZeroUsizePlus1(1).to_string(), "1");
   3076         assert_eq!(NonZeroUsizePlus1(2).to_string(), "2");
   3077         assert_eq!(NonZeroUsizePlus1(10).to_string(), "10");
   3078     }
   3079 }