ci-cargo

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

commit ef97d24fd55028b25236c0ef5f65f751b9a61d78
parent 0fdff6ef4c5b61bd82ee87ddab66a816d25644b4
Author: Zack Newman <zack@philomathiclife.com>
Date:   Sat,  6 Jun 2026 13:12:19 -0600

update msrv and deps. unveil /dev/null

Diffstat:
MCargo.toml | 25+++++++++++++++++++------
Msrc/args.rs | 823+------------------------------------------------------------------------------
Asrc/args/tests.rs | 817+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cargo.rs | 77+++--------------------------------------------------------------------------
Asrc/cargo/tests.rs | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.rs | 3++-
Msrc/manifest.rs | 1971+------------------------------------------------------------------------------
Asrc/manifest/tests.rs | 1950+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 2868 insertions(+), 2869 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0" name = "ci-cargo" readme = "README.md" repository = "https://git.philomathiclife.com/repos/ci-cargo/" -rust-version = "1.93.1" -version = "0.2.1" +rust-version = "1.96.0" +version = "0.2.2" [lints.rust] deprecated-safe = { level = "deny", priority = -1 } @@ -33,6 +33,7 @@ deref-into-dyn-supertrait = { level = "deny", priority = -1 } ffi-unwind-calls = { level = "deny", priority = -1 } #fuzzy-provenance-casts = { level = "deny", priority = -1 } impl-trait-redundant-captures = { level = "deny", priority = -1 } +linker-info = { level = "deny", priority = -1 } linker-messages = { level = "deny", priority = -1 } #lossy-provenance-casts = { level = "deny", priority = -1 } macro-use-extern-crate = { level = "deny", priority = -1 } @@ -66,6 +67,7 @@ variant-size-differences = { level = "deny", priority = -1 } # Before publishing to crates.io, comment above and uncomment below. #warnings = { level = "allow", priority = -1 } #ambiguous-associated-items = { level = "allow", priority = -1 } +#ambiguous-glob-imports = { level = "allow", priority = -1 } #arithmetic-overflow = { level = "allow", priority = -1 } #binary-asm-labels = { level = "allow", priority = -1 } #bindings-with-variant-name = { level = "allow", priority = -1 } @@ -74,6 +76,7 @@ variant-size-differences = { level = "deny", priority = -1 } #default-overrides-default-fields = { level = "allow", priority = -1 } #dependency-on-unit-never-type-fallback = { level = "allow", priority = -1 } #deref-nullptr = { level = "allow", priority = -1 } +#duplicate-features = { level = "allow", priority = -1 } #elided-lifetimes-in-associated-constant = { level = "allow", priority = -1 } #enum-intrinsics-non-enums = { level = "allow", priority = -1 } #explicit-builtin-cfgs-in-flags = { level = "allow", priority = -1 } @@ -101,16 +104,15 @@ variant-size-differences = { level = "deny", priority = -1 } #pub-use-of-private-extern-crate = { level = "allow", priority = -1 } #repr-transparent-non-zst-fields = { level = "allow", priority = -1 } #semicolon-in-expressions-from-macros = { level = "allow", priority = -1 } -#soft-unstable = { level = "allow", priority = -1 } #test-unstable-lint = { level = "allow", priority = -1 } #text-direction-codepoint-in-comment = { level = "allow", priority = -1 } #text-direction-codepoint-in-literal = { level = "allow", priority = -1 } #unconditional-panic = { level = "allow", priority = -1 } #undropped-manually-drops = { level = "allow", priority = -1 } +#uninhabited-static = { level = "allow", priority = -1 } #unknown-crate-types = { level = "allow", priority = -1 } #useless-deprecated = { level = "allow", priority = -1 } -# Before publishing to crates.io, comment below. [lints.clippy] cargo = { level = "deny", priority = -1 } complexity = { level = "deny", priority = -1 } @@ -133,9 +135,20 @@ question_mark_used = "allow" ref_patterns = "allow" redundant_pub_crate = "allow" return_and_then = "allow" +self_named_module_files = "allow" single_call_fn = "allow" single_char_lifetime_names = "allow" unseparated_literal_suffix = "allow" +# Before publishing to crates.io, comment above and uncomment below. +#cargo = "allow" +#complexity = "allow" +#correctness = "allow" +#nursery = "allow" +#pedantic = "allow" +#perf = "allow" +#restriction = "allow" +#style = "allow" +#suspicious = "allow" [lints.rustdoc] # Before publishing to crates.io, comment below and uncomment below that. @@ -158,10 +171,10 @@ targets = [ ] [dependencies] -toml = { version = "1.0.6", default-features = false, features = ["parse"] } +toml = { version = "1.1.2", default-features = false, features = ["parse"] } [target.'cfg(target_os = "openbsd")'.dependencies] -priv_sep = { version = "3.0.0-alpha.5.0", default-features = false, features = ["std"] } +priv_sep = { version = "3.0.0-alpha.5.1", default-features = false, features = ["std"] } [profile.release] codegen-units = 1 diff --git a/src/args.rs b/src/args.rs @@ -12,6 +12,9 @@ use std::{ path::PathBuf, time::Instant, }; +/// Unit tests. +#[cfg(test)] +mod tests; /// Help message. pub(crate) const HELP_MSG: &str = "Continuous integration of all features using cargo @@ -2257,823 +2260,3 @@ impl MetaCmd { }) } } -#[cfg(test)] -mod tests { - use super::{ - ArgsErr, CheckClippyTargets, Cmd, Ignored, MetaCmd, NonZeroUsizePlus1, Opts, OsString, - PathBuf, Target, Targets, TestTargets, - }; - use core::iter; - #[cfg(unix)] - use std::os::unix::ffi::OsStringExt as _; - #[expect( - clippy::cognitive_complexity, - clippy::too_many_lines, - reason = "want to test for a lot of things" - )] - #[test] - fn arg_parsing() { - assert_eq!(MetaCmd::from_args(iter::empty()), Err(ArgsErr::NoArgs)); - assert_eq!( - MetaCmd::from_args(iter::once(OsString::new())), - Err(ArgsErr::NoCommand) - ); - assert_eq!( - MetaCmd::from_args([OsString::new(), OsString::new()].into_iter()), - Err(ArgsErr::NoCommand) - ); - // Invalid UTF-8 errors gracefully. - #[cfg(unix)] - assert_eq!( - MetaCmd::from_args([OsString::new(), OsString::from_vec(vec![255])].into_iter()), - Err(ArgsErr::UnknownArg(OsString::from_vec(vec![255]))) - ); - // Whitespace is not ignored. - assert_eq!( - MetaCmd::from_args([OsString::new(), " clippy".to_owned().into()].into_iter()), - Err(ArgsErr::NoCommand) - ); - // We parse in a case-sensitive way. - assert_eq!( - MetaCmd::from_args([OsString::new(), "Clippy".to_owned().into()].into_iter()), - Err(ArgsErr::NoCommand) - ); - // We require options to be after the command (if one was passed). - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "--summary".to_owned().into(), - "clippy".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::NoCommand) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "help".to_owned().into(), - "--summary".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::HelpWithArgs) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "version".to_owned().into(), - "foo".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::VersionWithArgs) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--cargo-path".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::MissingCargoPath) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--cargo-home".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::MissingCargoHome) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--rustup-home".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::MissingRustupHome) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--deny-warnings".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--deny-warnings".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--deny-warnings".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--deny-warnings".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--ignored".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--ignored".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignored".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--ignored".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--include-ignored".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--include-ignored".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--include-ignored".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--include-ignored".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--ignored".to_owned().into(), - "--include-ignored".to_owned().into() - ] - .into_iter() - ), - Err(ArgsErr::IgnoredIncludeIgnored) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--ignore-features".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::MissingIgnoredFeatures) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--ignore-features".to_owned().into(), - ",".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateIgnoredFeatures(",".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignore-features".to_owned().into(), - "a,,a".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateIgnoredFeatures("a,,a".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignore-features".to_owned().into(), - ",a,b,".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateIgnoredFeatures(",a,b,".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--ignore-features".to_owned().into(), - ",a,,b".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateIgnoredFeatures(",a,,b".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--ignore-features".to_owned().into(), - "a,b,,".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateIgnoredFeatures("a,b,,".to_owned().into())) - ); - // `--all-targets` can't be combined with other targets. - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--lib".to_owned().into(), - "--all-targets".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::AllTargets) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--lib".to_owned().into(), - "--all-targets".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::AllTargets) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--all-targets".to_owned().into(), - "test".to_owned().into(), - "--doc".to_owned().into(), - "--all-targets".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::AllTargets) - ); - // `--doc` can't be combined with other targets. - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--all-targets".to_owned().into(), - "--doc".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::Doc) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--doc".to_owned().into(), - "--lib".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::Doc) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--doc".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::UnknownArg("--doc".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--color".to_owned().into(), - "clippy".to_owned().into(), - "--color".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateOption("--color".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--ignored".to_owned().into(), - "--ignored".to_owned().into(), - ] - .into_iter() - ), - Err(ArgsErr::DuplicateOption("--ignored".to_owned().into())) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--all-targets".to_owned().into(), - "--allow-implied-features".to_owned().into(), - "--cargo-home".to_owned().into(), - "--ignored".to_owned().into(), - "--cargo-path".to_owned().into(), - "cargo".to_owned().into(), - "--color".to_owned().into(), - "--default-toolchain".to_owned().into(), - "--deny-warnings".to_owned().into(), - "--dir".to_owned().into(), - OsString::new(), - "--ignore-compile-errors".to_owned().into(), - "--ignore-features".to_owned().into(), - ",a".to_owned().into(), - "--ignore-msrv".to_owned().into(), - "--rustup-home".to_owned().into(), - "a".to_owned().into(), - "--progress".to_owned().into(), - "--skip-msrv".to_owned().into(), - "--summary".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Clippy(CheckClippyTargets::All, true,), - Opts { - exec_dir: Some(PathBuf::new()), - rustup_home: Some("a".to_owned().into()), - cargo_home: Some("--ignored".to_owned().into()), - cargo_path: "cargo/cargo".to_owned().into(), - color: true, - default_toolchain: true, - allow_implied_features: true, - ignore_compile_errors: true, - ignore_msrv: true, - progress: true, - skip_msrv: true, - summary: true, - ignore_features: vec![String::new(), "a".to_owned()], - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--all-targets".to_owned().into(), - "--deny-warnings".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Clippy(CheckClippyTargets::All, true,), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: Vec::new(), - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--allow-implied-features".to_owned().into(), - "--cargo-home".to_owned().into(), - "--ignored".to_owned().into(), - "--cargo-path".to_owned().into(), - "cargo".to_owned().into(), - "--color".to_owned().into(), - "--default-toolchain".to_owned().into(), - "--dir".to_owned().into(), - OsString::new(), - "--ignore-compile-errors".to_owned().into(), - "--ignore-features".to_owned().into(), - OsString::new(), - "--ignore-msrv".to_owned().into(), - "--ignored".to_owned().into(), - "--rustup-home".to_owned().into(), - OsString::new(), - "--progress".to_owned().into(), - "--skip-msrv".to_owned().into(), - "--summary".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Test(TestTargets::Default, Ignored::Only), - Opts { - exec_dir: Some(PathBuf::new()), - rustup_home: Some(PathBuf::new()), - cargo_home: Some("--ignored".to_owned().into()), - cargo_path: "cargo/cargo".to_owned().into(), - color: true, - default_toolchain: true, - allow_implied_features: true, - ignore_compile_errors: true, - ignore_msrv: true, - progress: true, - skip_msrv: true, - summary: true, - ignore_features: vec![String::new()], - } - )) - ); - assert_eq!( - MetaCmd::from_args([OsString::new(), "test".to_owned().into(),].into_iter()), - Ok(MetaCmd::Cargo( - Cmd::Test(TestTargets::Default, Ignored::None), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: Vec::new(), - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "test".to_owned().into(), - "--include-ignored".to_owned().into() - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Test(TestTargets::Default, Ignored::Include), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: Vec::new(), - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--all-targets".to_owned().into(), - "--allow-implied-features".to_owned().into(), - "--cargo-home".to_owned().into(), - "--ignored".to_owned().into(), - "--cargo-path".to_owned().into(), - "cargo".to_owned().into(), - "--color".to_owned().into(), - "--default-toolchain".to_owned().into(), - "--dir".to_owned().into(), - OsString::new(), - "--ignore-compile-errors".to_owned().into(), - "--ignore-features".to_owned().into(), - "a,".to_owned().into(), - "--ignore-msrv".to_owned().into(), - "--rustup-home".to_owned().into(), - OsString::new(), - "--progress".to_owned().into(), - "--skip-msrv".to_owned().into(), - "--summary".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Check(CheckClippyTargets::All), - Opts { - exec_dir: Some(PathBuf::new()), - rustup_home: Some(PathBuf::new()), - cargo_home: Some("--ignored".to_owned().into()), - cargo_path: "cargo/cargo".to_owned().into(), - color: true, - default_toolchain: true, - allow_implied_features: true, - ignore_compile_errors: true, - ignore_msrv: true, - progress: true, - skip_msrv: true, - summary: true, - ignore_features: vec!["a".to_owned(), String::new()], - } - )) - ); - assert_eq!( - MetaCmd::from_args([OsString::new(), "check".to_owned().into(),].into_iter()), - Ok(MetaCmd::Cargo( - Cmd::Check(CheckClippyTargets::Default), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: Vec::new(), - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignore-features".to_owned().into(), - "a,,b".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Check(CheckClippyTargets::Default), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: vec!["a".to_owned(), String::new(), "b".to_owned()], - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignore-features".to_owned().into(), - "a,b,".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Check(CheckClippyTargets::Default), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: vec!["a".to_owned(), "b".to_owned(), String::new()], - } - )) - ); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignore-features".to_owned().into(), - "a,b".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Check(CheckClippyTargets::Default), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: vec!["a".to_owned(), "b".to_owned()], - } - )) - ); - // No whitespace cleanup is done on the features. - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "check".to_owned().into(), - "--ignore-features".to_owned().into(), - "a , , b, ".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::Check(CheckClippyTargets::Default), - Opts { - exec_dir: None, - rustup_home: None, - cargo_home: None, - cargo_path: "cargo".to_owned().into(), - color: false, - default_toolchain: false, - allow_implied_features: false, - ignore_compile_errors: false, - ignore_msrv: false, - progress: false, - skip_msrv: false, - summary: false, - ignore_features: vec![ - "a ".to_owned(), - " ".to_owned(), - " b".to_owned(), - " ".to_owned() - ], - } - )) - ); - assert_eq!( - MetaCmd::from_args([OsString::new(), "help".to_owned().into(),].into_iter()), - Ok(MetaCmd::Help) - ); - assert_eq!( - MetaCmd::from_args([OsString::new(), "version".to_owned().into(),].into_iter()), - Ok(MetaCmd::Version) - ); - let mut check_targets = Targets::new(Target::Examples); - assert!(check_targets.add(Target::Tests)); - assert!(!check_targets.add(Target::Examples)); - let mut test_targets = Targets::new(Target::Benches); - assert!(test_targets.add(Target::Bins)); - assert!(!test_targets.add(Target::Bins)); - assert!(test_targets.add(Target::Examples)); - assert!(test_targets.add(Target::Lib)); - assert!(test_targets.add(Target::Tests)); - assert_eq!( - MetaCmd::from_args( - [ - OsString::new(), - "clippy".to_owned().into(), - "--all-targets".to_owned().into(), - "--allow-implied-features".to_owned().into(), - "--cargo-home".to_owned().into(), - "--ignored".to_owned().into(), - "--cargo-path".to_owned().into(), - "cargo".to_owned().into(), - "--color".to_owned().into(), - "--default-toolchain".to_owned().into(), - "--deny-warnings".to_owned().into(), - "--dir".to_owned().into(), - OsString::new(), - "--ignore-compile-errors".to_owned().into(), - "--ignore-features".to_owned().into(), - ",a".to_owned().into(), - "--ignore-msrv".to_owned().into(), - "--rustup-home".to_owned().into(), - "a".to_owned().into(), - "test".to_owned().into(), - "--benches".to_owned().into(), - "--bins".to_owned().into(), - "--examples".to_owned().into(), - "--include-ignored".to_owned().into(), - "--lib".to_owned().into(), - "--tests".to_owned().into(), - "--progress".to_owned().into(), - "--skip-msrv".to_owned().into(), - "check".to_owned().into(), - "--tests".to_owned().into(), - "--examples".to_owned().into(), - "--summary".to_owned().into(), - ] - .into_iter() - ), - Ok(MetaCmd::Cargo( - Cmd::CheckClippyTest( - CheckClippyTargets::Targets(check_targets), - CheckClippyTargets::All, - true, - TestTargets::Targets(test_targets), - Ignored::Include, - ), - Opts { - exec_dir: Some(PathBuf::new()), - rustup_home: Some("a".to_owned().into()), - cargo_home: Some("--ignored".to_owned().into()), - cargo_path: "cargo/cargo".to_owned().into(), - color: true, - default_toolchain: true, - allow_implied_features: true, - ignore_compile_errors: true, - ignore_msrv: true, - progress: true, - skip_msrv: true, - summary: true, - ignore_features: vec![String::new(), "a".to_owned()], - } - )) - ); - } - #[test] - fn non_zero_usize_plus_1() { - #[cfg(target_pointer_width = "64")] - assert_eq!(NonZeroUsizePlus1(0).to_string(), "18446744073709551616"); - #[cfg(target_pointer_width = "64")] - assert_eq!( - NonZeroUsizePlus1(usize::MAX).to_string(), - "18446744073709551615" - ); - #[cfg(target_pointer_width = "32")] - assert_eq!(NonZeroUsizePlus1(0).to_string(), "4294967296"); - #[cfg(target_pointer_width = "32")] - assert_eq!(NonZeroUsizePlus1(usize::MAX).to_string(), "4294967295"); - #[cfg(target_pointer_width = "16")] - assert_eq!(NonZeroUsizePlus1(0).to_string(), "65536"); - #[cfg(target_pointer_width = "16")] - assert_eq!(NonZeroUsizePlus1(usize::MAX).to_string(), "65535"); - assert_eq!(NonZeroUsizePlus1(1).to_string(), "1"); - assert_eq!(NonZeroUsizePlus1(2).to_string(), "2"); - assert_eq!(NonZeroUsizePlus1(10).to_string(), "10"); - } -} diff --git a/src/args/tests.rs b/src/args/tests.rs @@ -0,0 +1,817 @@ +use super::{ + ArgsErr, CheckClippyTargets, Cmd, Ignored, MetaCmd, NonZeroUsizePlus1, Opts, OsString, PathBuf, + Target, Targets, TestTargets, +}; +use core::iter; +#[cfg(unix)] +use std::os::unix::ffi::OsStringExt as _; +#[expect( + clippy::cognitive_complexity, + clippy::too_many_lines, + reason = "want to test for a lot of things" +)] +#[test] +fn arg_parsing() { + assert_eq!(MetaCmd::from_args(iter::empty()), Err(ArgsErr::NoArgs)); + assert_eq!( + MetaCmd::from_args(iter::once(OsString::new())), + Err(ArgsErr::NoCommand) + ); + assert_eq!( + MetaCmd::from_args([OsString::new(), OsString::new()].into_iter()), + Err(ArgsErr::NoCommand) + ); + // Invalid UTF-8 errors gracefully. + #[cfg(unix)] + assert_eq!( + MetaCmd::from_args([OsString::new(), OsString::from_vec(vec![255])].into_iter()), + Err(ArgsErr::UnknownArg(OsString::from_vec(vec![255]))) + ); + // Whitespace is not ignored. + assert_eq!( + MetaCmd::from_args([OsString::new(), " clippy".to_owned().into()].into_iter()), + Err(ArgsErr::NoCommand) + ); + // We parse in a case-sensitive way. + assert_eq!( + MetaCmd::from_args([OsString::new(), "Clippy".to_owned().into()].into_iter()), + Err(ArgsErr::NoCommand) + ); + // We require options to be after the command (if one was passed). + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "--summary".to_owned().into(), + "clippy".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::NoCommand) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "help".to_owned().into(), + "--summary".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::HelpWithArgs) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "version".to_owned().into(), + "foo".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::VersionWithArgs) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--cargo-path".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::MissingCargoPath) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--cargo-home".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::MissingCargoHome) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--rustup-home".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::MissingRustupHome) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--deny-warnings".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--deny-warnings".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--deny-warnings".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--deny-warnings".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--ignored".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--ignored".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignored".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--ignored".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--include-ignored".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--include-ignored".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--include-ignored".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--include-ignored".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--ignored".to_owned().into(), + "--include-ignored".to_owned().into() + ] + .into_iter() + ), + Err(ArgsErr::IgnoredIncludeIgnored) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--ignore-features".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::MissingIgnoredFeatures) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--ignore-features".to_owned().into(), + ",".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateIgnoredFeatures(",".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignore-features".to_owned().into(), + "a,,a".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateIgnoredFeatures("a,,a".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignore-features".to_owned().into(), + ",a,b,".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateIgnoredFeatures(",a,b,".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--ignore-features".to_owned().into(), + ",a,,b".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateIgnoredFeatures(",a,,b".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--ignore-features".to_owned().into(), + "a,b,,".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateIgnoredFeatures("a,b,,".to_owned().into())) + ); + // `--all-targets` can't be combined with other targets. + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--lib".to_owned().into(), + "--all-targets".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::AllTargets) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--lib".to_owned().into(), + "--all-targets".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::AllTargets) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--all-targets".to_owned().into(), + "test".to_owned().into(), + "--doc".to_owned().into(), + "--all-targets".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::AllTargets) + ); + // `--doc` can't be combined with other targets. + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--all-targets".to_owned().into(), + "--doc".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::Doc) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--doc".to_owned().into(), + "--lib".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::Doc) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--doc".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::UnknownArg("--doc".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--color".to_owned().into(), + "clippy".to_owned().into(), + "--color".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateOption("--color".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--ignored".to_owned().into(), + "--ignored".to_owned().into(), + ] + .into_iter() + ), + Err(ArgsErr::DuplicateOption("--ignored".to_owned().into())) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--all-targets".to_owned().into(), + "--allow-implied-features".to_owned().into(), + "--cargo-home".to_owned().into(), + "--ignored".to_owned().into(), + "--cargo-path".to_owned().into(), + "cargo".to_owned().into(), + "--color".to_owned().into(), + "--default-toolchain".to_owned().into(), + "--deny-warnings".to_owned().into(), + "--dir".to_owned().into(), + OsString::new(), + "--ignore-compile-errors".to_owned().into(), + "--ignore-features".to_owned().into(), + ",a".to_owned().into(), + "--ignore-msrv".to_owned().into(), + "--rustup-home".to_owned().into(), + "a".to_owned().into(), + "--progress".to_owned().into(), + "--skip-msrv".to_owned().into(), + "--summary".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Clippy(CheckClippyTargets::All, true,), + Opts { + exec_dir: Some(PathBuf::new()), + rustup_home: Some("a".to_owned().into()), + cargo_home: Some("--ignored".to_owned().into()), + cargo_path: "cargo/cargo".to_owned().into(), + color: true, + default_toolchain: true, + allow_implied_features: true, + ignore_compile_errors: true, + ignore_msrv: true, + progress: true, + skip_msrv: true, + summary: true, + ignore_features: vec![String::new(), "a".to_owned()], + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--all-targets".to_owned().into(), + "--deny-warnings".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Clippy(CheckClippyTargets::All, true,), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: Vec::new(), + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--allow-implied-features".to_owned().into(), + "--cargo-home".to_owned().into(), + "--ignored".to_owned().into(), + "--cargo-path".to_owned().into(), + "cargo".to_owned().into(), + "--color".to_owned().into(), + "--default-toolchain".to_owned().into(), + "--dir".to_owned().into(), + OsString::new(), + "--ignore-compile-errors".to_owned().into(), + "--ignore-features".to_owned().into(), + OsString::new(), + "--ignore-msrv".to_owned().into(), + "--ignored".to_owned().into(), + "--rustup-home".to_owned().into(), + OsString::new(), + "--progress".to_owned().into(), + "--skip-msrv".to_owned().into(), + "--summary".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Test(TestTargets::Default, Ignored::Only), + Opts { + exec_dir: Some(PathBuf::new()), + rustup_home: Some(PathBuf::new()), + cargo_home: Some("--ignored".to_owned().into()), + cargo_path: "cargo/cargo".to_owned().into(), + color: true, + default_toolchain: true, + allow_implied_features: true, + ignore_compile_errors: true, + ignore_msrv: true, + progress: true, + skip_msrv: true, + summary: true, + ignore_features: vec![String::new()], + } + )) + ); + assert_eq!( + MetaCmd::from_args([OsString::new(), "test".to_owned().into(),].into_iter()), + Ok(MetaCmd::Cargo( + Cmd::Test(TestTargets::Default, Ignored::None), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: Vec::new(), + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "test".to_owned().into(), + "--include-ignored".to_owned().into() + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Test(TestTargets::Default, Ignored::Include), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: Vec::new(), + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--all-targets".to_owned().into(), + "--allow-implied-features".to_owned().into(), + "--cargo-home".to_owned().into(), + "--ignored".to_owned().into(), + "--cargo-path".to_owned().into(), + "cargo".to_owned().into(), + "--color".to_owned().into(), + "--default-toolchain".to_owned().into(), + "--dir".to_owned().into(), + OsString::new(), + "--ignore-compile-errors".to_owned().into(), + "--ignore-features".to_owned().into(), + "a,".to_owned().into(), + "--ignore-msrv".to_owned().into(), + "--rustup-home".to_owned().into(), + OsString::new(), + "--progress".to_owned().into(), + "--skip-msrv".to_owned().into(), + "--summary".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Check(CheckClippyTargets::All), + Opts { + exec_dir: Some(PathBuf::new()), + rustup_home: Some(PathBuf::new()), + cargo_home: Some("--ignored".to_owned().into()), + cargo_path: "cargo/cargo".to_owned().into(), + color: true, + default_toolchain: true, + allow_implied_features: true, + ignore_compile_errors: true, + ignore_msrv: true, + progress: true, + skip_msrv: true, + summary: true, + ignore_features: vec!["a".to_owned(), String::new()], + } + )) + ); + assert_eq!( + MetaCmd::from_args([OsString::new(), "check".to_owned().into(),].into_iter()), + Ok(MetaCmd::Cargo( + Cmd::Check(CheckClippyTargets::Default), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: Vec::new(), + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignore-features".to_owned().into(), + "a,,b".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Check(CheckClippyTargets::Default), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: vec!["a".to_owned(), String::new(), "b".to_owned()], + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignore-features".to_owned().into(), + "a,b,".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Check(CheckClippyTargets::Default), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: vec!["a".to_owned(), "b".to_owned(), String::new()], + } + )) + ); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignore-features".to_owned().into(), + "a,b".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Check(CheckClippyTargets::Default), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: vec!["a".to_owned(), "b".to_owned()], + } + )) + ); + // No whitespace cleanup is done on the features. + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "check".to_owned().into(), + "--ignore-features".to_owned().into(), + "a , , b, ".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::Check(CheckClippyTargets::Default), + Opts { + exec_dir: None, + rustup_home: None, + cargo_home: None, + cargo_path: "cargo".to_owned().into(), + color: false, + default_toolchain: false, + allow_implied_features: false, + ignore_compile_errors: false, + ignore_msrv: false, + progress: false, + skip_msrv: false, + summary: false, + ignore_features: vec![ + "a ".to_owned(), + " ".to_owned(), + " b".to_owned(), + " ".to_owned() + ], + } + )) + ); + assert_eq!( + MetaCmd::from_args([OsString::new(), "help".to_owned().into(),].into_iter()), + Ok(MetaCmd::Help) + ); + assert_eq!( + MetaCmd::from_args([OsString::new(), "version".to_owned().into(),].into_iter()), + Ok(MetaCmd::Version) + ); + let mut check_targets = Targets::new(Target::Examples); + assert!(check_targets.add(Target::Tests)); + assert!(!check_targets.add(Target::Examples)); + let mut test_targets = Targets::new(Target::Benches); + assert!(test_targets.add(Target::Bins)); + assert!(!test_targets.add(Target::Bins)); + assert!(test_targets.add(Target::Examples)); + assert!(test_targets.add(Target::Lib)); + assert!(test_targets.add(Target::Tests)); + assert_eq!( + MetaCmd::from_args( + [ + OsString::new(), + "clippy".to_owned().into(), + "--all-targets".to_owned().into(), + "--allow-implied-features".to_owned().into(), + "--cargo-home".to_owned().into(), + "--ignored".to_owned().into(), + "--cargo-path".to_owned().into(), + "cargo".to_owned().into(), + "--color".to_owned().into(), + "--default-toolchain".to_owned().into(), + "--deny-warnings".to_owned().into(), + "--dir".to_owned().into(), + OsString::new(), + "--ignore-compile-errors".to_owned().into(), + "--ignore-features".to_owned().into(), + ",a".to_owned().into(), + "--ignore-msrv".to_owned().into(), + "--rustup-home".to_owned().into(), + "a".to_owned().into(), + "test".to_owned().into(), + "--benches".to_owned().into(), + "--bins".to_owned().into(), + "--examples".to_owned().into(), + "--include-ignored".to_owned().into(), + "--lib".to_owned().into(), + "--tests".to_owned().into(), + "--progress".to_owned().into(), + "--skip-msrv".to_owned().into(), + "check".to_owned().into(), + "--tests".to_owned().into(), + "--examples".to_owned().into(), + "--summary".to_owned().into(), + ] + .into_iter() + ), + Ok(MetaCmd::Cargo( + Cmd::CheckClippyTest( + CheckClippyTargets::Targets(check_targets), + CheckClippyTargets::All, + true, + TestTargets::Targets(test_targets), + Ignored::Include, + ), + Opts { + exec_dir: Some(PathBuf::new()), + rustup_home: Some("a".to_owned().into()), + cargo_home: Some("--ignored".to_owned().into()), + cargo_path: "cargo/cargo".to_owned().into(), + color: true, + default_toolchain: true, + allow_implied_features: true, + ignore_compile_errors: true, + ignore_msrv: true, + progress: true, + skip_msrv: true, + summary: true, + ignore_features: vec![String::new(), "a".to_owned()], + } + )) + ); +} +#[test] +fn non_zero_usize_plus_1() { + #[cfg(target_pointer_width = "64")] + assert_eq!(NonZeroUsizePlus1(0).to_string(), "18446744073709551616"); + #[cfg(target_pointer_width = "64")] + assert_eq!( + NonZeroUsizePlus1(usize::MAX).to_string(), + "18446744073709551615" + ); + #[cfg(target_pointer_width = "32")] + assert_eq!(NonZeroUsizePlus1(0).to_string(), "4294967296"); + #[cfg(target_pointer_width = "32")] + assert_eq!(NonZeroUsizePlus1(usize::MAX).to_string(), "4294967295"); + #[cfg(target_pointer_width = "16")] + assert_eq!(NonZeroUsizePlus1(0).to_string(), "65536"); + #[cfg(target_pointer_width = "16")] + assert_eq!(NonZeroUsizePlus1(usize::MAX).to_string(), "65535"); + assert_eq!(NonZeroUsizePlus1(1).to_string(), "1"); + assert_eq!(NonZeroUsizePlus1(2).to_string(), "2"); + assert_eq!(NonZeroUsizePlus1(10).to_string(), "10"); +} diff --git a/src/cargo.rs b/src/cargo.rs @@ -8,6 +8,9 @@ use std::{ path::{Path, PathBuf}, process::{Command, Stdio}, }; +/// Unit tests. +#[cfg(test)] +mod tests; /// Error returned from [`Toolchain::get_version`]. pub(crate) enum ToolchainErr { /// [`Command::output`] erred with the contained `Error` for the contained `Command`. @@ -714,77 +717,3 @@ impl Test { execute_command(c, options, features) } } -#[cfg(test)] -mod tests { - use super::{Command, Toolchain, ToolchainErr, Version}; - #[expect(clippy::cognitive_complexity, reason = "a lot of tests")] - #[test] - fn toolchain_parse() { - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), vec![255]), Err(e) if matches!(*e, ToolchainErr::StdoutNotUtf8(_))) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), Vec::new()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v.is_empty())) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3.".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3.")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3a".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3a")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b" cargo 1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == " cargo 1.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"Cargo 1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "Cargo 1.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.00.0".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.00.0")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.03".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.03")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo -1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo -1.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo1.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo\t1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo\t1.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1..3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1..3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 111111111111111111111111.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 111111111111111111111111.2.3")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3.4".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3.4")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3-nightly".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3-nightly")) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 18446744073709551615.18446744073709551615.18446744073709551615".to_vec()), Ok(v) if v == Version { major: u64::MAX, minor: u64::MAX, patch: u64::MAX, }) - ); - assert!( - matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 0.0.0 asdflk 0023n0=lk0932(!@#V)\x00".to_vec()), Ok(v) if v == Version { major: 0, minor: 0, patch: 0, }) - ); - } -} diff --git a/src/cargo/tests.rs b/src/cargo/tests.rs @@ -0,0 +1,71 @@ +use super::{Command, Toolchain, ToolchainErr, Version}; +#[expect(clippy::cognitive_complexity, reason = "a lot of tests")] +#[test] +fn toolchain_parse() { + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), vec![255]), Err(e) if matches!(*e, ToolchainErr::StdoutNotUtf8(_))) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), Vec::new()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v.is_empty())) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3.".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3.")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3a".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3a")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b" cargo 1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == " cargo 1.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"Cargo 1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "Cargo 1.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.00.0".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.00.0")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.03".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.03")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo -1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo -1.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo1.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo\t1.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo\t1.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1..3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1..3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 111111111111111111111111.2.3".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 111111111111111111111111.2.3")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3.4".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3.4")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 1.2.3-nightly".to_vec()), Err(e) if matches!(*e, ToolchainErr::UnexpectedOutput(_, ref v) if v == "cargo 1.2.3-nightly")) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 18446744073709551615.18446744073709551615.18446744073709551615".to_vec()), Ok(v) if v == Version { major: u64::MAX, minor: u64::MAX, patch: u64::MAX, }) + ); + assert!( + matches!(Toolchain::parse_stdout(Command::new(""), b"cargo 0.0.0 asdflk 0023n0=lk0932(!@#V)\x00".to_vec()), Ok(v) if v == Version { major: 0, minor: 0, patch: 0, }) + ); +} diff --git a/src/main.rs b/src/main.rs @@ -182,7 +182,7 @@ const fn priv_sep_final<Never>(_: &Path) -> Result<(), Never> { Ok(()) } /// Removes read permissions to entire file system before allowing execute permissions to `cargo_path` or `ROOT`. -/// Last remove `flock rpath unveil` from `pledge(2)`. +/// Adds read permissions for `"/dev/null"`. Last remove `flock rpath unveil` from `pledge(2)`. #[cfg(target_os = "openbsd")] fn priv_sep_final(cargo_path: &Path) -> Result<(), E> { Permissions::NONE @@ -194,6 +194,7 @@ fn priv_sep_final(cargo_path: &Path) -> Result<(), E> { } else { Permissions::EXECUTE.unveil(ROOT) } + .and_then(|()| Permissions::READ.unveil(c"/dev/null")) .map_err(E::Unveil) .and_then(|()| Promises::pledge_raw(c"exec proc stdio").map_err(E::Pledge)) }) diff --git a/src/manifest.rs b/src/manifest.rs @@ -14,6 +14,9 @@ use toml::{ de::{DeArray, DeValue, Error as TomlErr}, map::Map, }; +/// Unit tests. +#[cfg(test)] +mod tests; /// `"workspace"`. const WORKSPACE: &str = "workspace"; /// `"package"`. @@ -1784,1971 +1787,3 @@ impl Manifest { }) } } -#[cfg(test)] -mod tests { - use super::{ - DependenciesErr, FeatureDependenciesErr, Features, FeaturesErr, ImpliedFeaturesErr, - Manifest, ManifestErr, Msrv, NonZeroUsizePlus1, Package, PackageErr, Path, PathBuf, - PowerSet, TooManyFeaturesErr, TryLockError, WorkspaceErr, - }; - impl PartialEq for PackageErr { - #[expect(clippy::cognitive_complexity, reason = "long match expression")] - fn eq(&self, other: &Self) -> bool { - match *self { - Self::Missing => matches!(*other, Self::Missing), - Self::InvalidType => matches!(*other, Self::InvalidType), - Self::MissingName => matches!(*other, Self::MissingName), - Self::InvalidNameType => matches!(*other, Self::InvalidNameType), - Self::InvalidMsrvType => matches!(*other, Self::InvalidMsrvType), - Self::Msrv => matches!(*other, Self::Msrv), - Self::MsrvWorkspaceMissing => matches!(*other, Self::MsrvWorkspaceMissing), - Self::MsrvWorkspaceVal => matches!(*other, Self::MsrvWorkspaceVal), - Self::InvalidWorkspaceType => matches!(*other, Self::InvalidWorkspaceType), - Self::WorkspaceIo(ref e) => { - matches!(*other, Self::WorkspaceIo(ref e2) if e.kind() == e2.kind()) - } - Self::WorkspaceLock(ref e) => { - matches!(*other, Self::WorkspaceLock(ref e2) if match *e { - TryLockError::Error(ref inner_e) => matches!(*e2, TryLockError::Error(ref inner_e2) if inner_e.kind() == inner_e2.kind()), - TryLockError::WouldBlock => matches!(*e2, TryLockError::WouldBlock), - }) - } - Self::WorkspaceLenMismatch => matches!(*other, Self::WorkspaceLenMismatch), - Self::WorkspaceDoesNotExist => matches!(*other, Self::WorkspaceDoesNotExist), - Self::WorkspaceRead(ref e, ref p) => { - matches!(*other, Self::WorkspaceRead(ref e2, ref p2) if e.kind() == e2.kind() && p == p2) - } - Self::WorkspaceReadLock(ref e, ref p) => { - matches!(*other, Self::WorkspaceReadLock(ref e2, ref p2) if p == p2 && match *e { - TryLockError::Error(ref inner_e) => matches!(*e2, TryLockError::Error(ref inner_e2) if inner_e.kind() == inner_e2.kind()), - TryLockError::WouldBlock => matches!(*e2, TryLockError::WouldBlock), - }) - } - Self::WorkspaceReadLenMismatch(ref p) => { - matches!(*other, Self::WorkspaceReadLenMismatch(ref p2) if p == p2) - } - Self::WorkspaceToml(ref e, ref p) => { - matches!(*other, Self::WorkspaceToml(ref e2, ref p2) if e == e2 && p == p2) - } - Self::Workspace(e, ref p) => { - matches!(*other, Self::Workspace(e2, ref p2) if e == e2 && p == p2) - } - } - } - } - #[expect( - clippy::cognitive_complexity, - clippy::too_many_lines, - reason = "want to test a lot of things" - )] - #[test] - fn cargo_toml() { - assert!( - Manifest::from_toml("a".to_owned(), false, Path::new(""), &[]) - .map_or_else(|e| matches!(*e, ManifestErr::Toml(_, _)), |_| false) - ); - assert_eq!( - Manifest::from_toml(String::new(), false, Path::new(""), &[]), - Err(Box::new(ManifestErr::Package( - PackageErr::Missing, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml("[' package']".to_owned(), false, Path::new(""), &[]), - Err(Box::new(ManifestErr::Package( - PackageErr::Missing, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml("['package ']".to_owned(), false, Path::new(""), &[]), - Err(Box::new(ManifestErr::Package( - PackageErr::Missing, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml("package=2".to_owned(), false, Path::new(""), &[]), - Err(Box::new(ManifestErr::Package( - PackageErr::InvalidType, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml("[package]".to_owned(), false, Path::new(""), &[]), - Err(Box::new(ManifestErr::Package( - PackageErr::MissingName, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml("[package]\nname=true".to_owned(), false, Path::new(""), &[]), - Err(Box::new(ManifestErr::Package( - PackageErr::InvalidNameType, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n\nrust-version=2".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::InvalidMsrvType, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"a\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"1.00.0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"1..0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"1.\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"01.0.0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"1.0.0.1\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"111111111111111111111111.2.3\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"1.0.0-nightly\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"-1.0.0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\" 1.0.0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"1.0.0 \"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Msrv, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version={}".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::MsrvWorkspaceMissing, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version={workspace=2}".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::MsrvWorkspaceVal, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version={workspace=false}".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::MsrvWorkspaceVal, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version={workspace=true}\nworkspace=2".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::InvalidWorkspaceType, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "workspace=2\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Workspace(WorkspaceErr::InvalidType, PathBuf::new()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[workspace]\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Workspace(WorkspaceErr::MissingPackage, PathBuf::new()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[workspace]\npackage=2\n[package]\nname=\"\"\nrust-version={workspace=true}" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Workspace(WorkspaceErr::InvalidPackageType, PathBuf::new()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[workspace.package]\n[package]\nname=\"\"\nrust-version={workspace=true}" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Workspace(WorkspaceErr::MissingPackageMsrv, PathBuf::new()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[workspace.package]\nrust-version={}\n[package]\nname=\"\"\nrust-version={workspace=true}" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Workspace(WorkspaceErr::InvalidPackageMsrvType, PathBuf::new()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[workspace.package]\nrust-version=\"\"\n[package]\nname=\"\"\nrust-version={workspace=true}" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Package( - PackageErr::Workspace(WorkspaceErr::Msrv, PathBuf::new()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "features=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::InvalidType, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"/\"=[]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::InvalidName("/".to_owned()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"dep:\"=[]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::InvalidName("dep:".to_owned()), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidFeatureType( - String::new() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[true]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependencyType( - String::new() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( - String::new(), - "foo".to_owned() - )), - PathBuf::new() - ))) - ); - // Feature dependencies can't be implied features when implied features are forbidden. - assert_eq!( - Manifest::from_toml( - "[dependencies]\nfoo={optional=true}\n[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( - String::new(), - "foo".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"\"]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( - String::new() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"a\"]\na=[\"\"]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( - String::new() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"a\"]\na=[\"b\"]\nb=[\"a\"]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( - "a".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"a\"]\na=[\"c\",\"b\"]\nb=[\"a\"]\nc=[]\n[package]\nname=\"\"" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( - "a".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[]\na=[\"c\",\"b\"]\nb=[\"a\"]\nc=[]\n[package]\nname=\"\"" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( - "a".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"a\",\"b\"]\na=[\"b\"]\nb=[]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( - String::new(), - "b".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"a\",\"a\"]\na=[]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( - String::new(), - "a".to_owned() - )), - PathBuf::new() - ))) - ); - // Duplicate `"dep:"` feature dependencies error. - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"dep:\",\"dep:\"]\na=[]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( - String::new(), - "dep:".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "target=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TargetType, - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "dependencies=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::Type("dependencies")), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "build-dependencies=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::Type("build-dependencies")), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( - "dependencies", - "dep:".to_owned() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( - "dependencies", - "/".to_owned() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[build-dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( - "build-dependencies", - "dep:".to_owned() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[build-dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( - "build-dependencies", - "/".to_owned() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[dependencies]\n\"\"=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::DependencyType( - "dependencies", - String::new() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[build-dependencies]\n\"\"=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::DependencyType( - "build-dependencies", - String::new() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::OptionalType( - "dependencies", - String::new() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[build-dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::OptionalType( - "build-dependencies", - String::new() - )), - PathBuf::new(), - ))) - ); - // Implied features are disallowed iff `!allow_implied_features`. - assert_eq!( - Manifest::from_toml( - "[dependencies]\nfoo={optional=true}\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::Dependencies(DependenciesErr::ImpliedFeature( - "dependencies", - "foo".to_owned() - )), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target]\n\"\"=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TargetPlatformType(String::new()), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\"]\ndependencies=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::Type("dependencies") - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\"]\nbuild-dependencies=2\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::Type("build-dependencies") - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::Name("dependencies", "/".to_owned()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::Name("dependencies", "dep:".to_owned()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".build-dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::Name("build-dependencies", "/".to_owned()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".build-dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::Name("build-dependencies", "dep:".to_owned()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".dependencies]\n\"\"=false\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::DependencyType("dependencies", String::new()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".build-dependencies]\n\"\"=false\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::DependencyType("build-dependencies", String::new()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::OptionalType("dependencies", String::new()) - ), - PathBuf::new(), - ))) - ); - assert_eq!( - Manifest::from_toml( - "[target.\"\".build-dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"" - .to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::TagetPlatformDependencies( - String::new(), - DependenciesErr::OptionalType("build-dependencies", String::new()) - ), - PathBuf::new(), - ))) - ); - // An invalid dependency error occurs later when we `allow_implied_features` since - // implied features aren't added until after feature extraction. - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), - true, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::ImpliedFeatures( - ImpliedFeaturesErr::InvalidDependency(String::new(), "foo".to_owned()), - PathBuf::new(), - ))) - ); - // In contrast, above would have erred sooner if `!allow_implied_features`. - assert_eq!( - Manifest::from_toml( - "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &[] - ), - Err(Box::new(ManifestErr::Features( - FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( - String::new(), - "foo".to_owned() - )), - PathBuf::new() - ))) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"".to_owned(), - false, - Path::new(""), - &["a".to_owned()] - ), - Err(Box::new(ManifestErr::UndefinedIgnoreFeature( - "a".to_owned(), - PathBuf::new() - ))) - ); - // Even if we forbid implied features, we don't error when a feature is defined - // with the same name of an implied feature. This is due to simplicity in code - // and the fact that `cargo` will error anyway. - // - // For example once `cargo` is invoked, an error will occur due to duplicate features: - // the explict feature `foo` and the implied feature from the dependency `foo`. - assert_eq!( - Manifest::from_toml( - "[dependencies]\nfoo={optional=true}\n[features]\nfoo=[]\n[package]\nname=\"\"" - .to_owned(), - false, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![("foo".to_owned(), Vec::new())]), - }) - ); - // Allow major-only MSRV. - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"foo\"\nrust-version=\"0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: Some(Msrv { - major: 0, - minor: None, - patch: None, - }), - name: "foo".to_owned(), - }, - features: Features(Vec::new()), - }) - ); - // Allow escapes. - assert_eq!( - Manifest::from_toml( - "[\"\\u0070ackage\"]\n\"n\\u0061me\"=\"\\u0066oo\"\n\"\\u0072ust-version\"=\"0\\u002E\\u0031\"".to_owned(), - false, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: Some(Msrv { - major: 0, - minor: Some(1), - patch: None, - }), - name: "foo".to_owned(), - }, - features: Features(Vec::new()), - }) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\nrust-version=\"0.0.0\"".to_owned(), - false, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: Some(Msrv { - major: 0, - minor: Some(0), - patch: Some(0), - }), - name: String::new(), - }, - features: Features(Vec::new()), - }) - ); - // Ignore non `rust-version` keys in `package`. Ignore keys in the root document except `package`, - // `features`, `dependencies`, `build-dependencies`, and `target`. Ignore keys in - // `target.<something>` unless the key is `dependencies` or `build-dependencies`. Don't treat - // `<something>` special in `target.<something>` other than its being a table. - assert_eq!( - Manifest::from_toml("dev-dependencies=2\n[package]\nname=\"\"\n\nfoo=2\nrust-version=\"18446744073709551615.18446744073709551615.18446744073709551615\"\n[foo]\nbar=false\n[target.\"\".foo]\nbar=2\n[target.foo]\nbar=false\n[target.dependencies]\nfoo=2\n[target.build-dependencies]\nfoo=false\n[target.dev-dependencies]\nfoo=true\n".to_owned(), false, Path::new(""), &[]), - Ok(Manifest { - package: Package { - msrv: Some(Msrv { - major: u64::MAX, - minor: Some(u64::MAX), - patch: Some(u64::MAX), - }), - name: String::new(), - }, - features: Features(Vec::new()), - }) - ); - // [package] - // name = "" - // - // ["\u0064ependencies"] - // "\u0000" = "\u0000" - // a = { optional = true } - // - // ["build-\u0064ependencies"] - // "\u0000" = { optional = true } - // - // [dev-dependencies] - // buzz = { optional = true } - // - // [target."".dependencies] - // b = { optional = false, foo = 2 } - // fizz = { optional = true, foo = 3 } - // - // [target.a.dependencies] - // c = { optional = true } - // wuzz = { optional = true } - // - // [features] - // default = ["bar","dep:lk","a/ak", "a/ak"] - // bar = ["dep\u003Awuzz"] - // - // We allow any and all key names unless it's the features table or a dependency table; in which case - // key names must not contain `'/'` nor begin with `"dep:"`. - // - // The order of features is based on the following hierarchy: - // * Explict features: lexicographically sorted - // * dependencies: optional only, lexicographically sorted, only if an explicit feature doesn't exist with - // the same name nor any explicit feature contains a dependency named `"dep:<dependecy>"` and we allow - // implied features. If such feature exists, we don't error but simply don't add. - // * build-dependencies: read above. - // * target.<something>: lexicographically sorted by something, within `something`, `dependencies` - // is first using the same methodology as item 2, last `build-dependencies`. - // - // Once the order of features is determined, the only feature dependencies that are retained are those - // that don't contain `'/'` nor begin with `"dep:"`. We don't require dependencies to be defined for - // feature dependencies that contain `'/'` or begin with `"dep:"`. We don't care about duplicate feature - // dependencies that contain `'/'`. - // - // Based on above, `Features` looks like the following: - // 1. (bar, []) - // 2. (default, ["bar"]) - // 3. (a, []) - // 4. (\x00, []) - // 5. (fizz, []) - // 6. (c, []) - assert_eq!( - Manifest::from_toml( - "[\"\\u0064ependencies\"]\n\"\\u0000\"=\"\\u0000\"\na={optional=true}\n[\"build-\\u0064ependencies\"]\n\"\\u0000\"={optional=true}\n[target.\"\".dependencies]\nb={optional=false,foo=2}\nfizz={optional=true,foo=3}\n[features]\ndefault=[\"bar\",\"dep:lk\",\"a/ak\",\"a/ak\"]\nbar=[\"dep\\u003Awuzz\"]\n[dev-dependencies]\nbuzz={optional=true}\n[target.a.dependencies]\nc={optional=true}\nwuzz={optional=true}\n[package]\nname=\"\"".to_owned(), - true, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![("bar".to_owned(), Vec::new()), ("default".to_owned(), vec!["bar".to_owned()]), ("a".to_owned(), Vec::new()), ("\0".to_owned(), Vec::new()), ("fizz".to_owned(), Vec::new()), ("c".to_owned(), Vec::new())]), - }) - ); - // [package] - // name = "" - // - // [dependencies] - // foo = { "optional" = true } - // fizz = { "optional" = true } - // - // [features] - // fizz = ["dep:fizz"] - // bar = ["dep:foo"] - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n[dependencies]\nfoo={optional=true}\nfizz={optional=true}\n[features]\nfizz=[\"dep:fizz\"]\nbar=[\"dep:foo\"]".to_owned(), - false, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![ - ("bar".to_owned(), Vec::new()), - ("fizz".to_owned(), Vec::new()) - ]), - }) - ); - // [package] - // name = "" - // - // [dependencies] - // bar = { "optional" = true } - // - // [features] - // foo = ["bar"] - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n[dependencies]\nbar={optional=true}\n[features]\nfoo=[\"bar\"]" - .to_owned(), - true, - Path::new(""), - &[] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![ - ("foo".to_owned(), vec!["bar".to_owned()]), - ("bar".to_owned(), Vec::new()), - ]), - }) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n[features]\na=[]\nb=[\"a\"]".to_owned(), - false, - Path::new(""), - &["a".to_owned()] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![]), - }) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n[features]\na=[]\nb=[\"a\"]".to_owned(), - false, - Path::new(""), - &["b".to_owned()] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![("a".to_owned(), Vec::new())]), - }) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n[dependencies]\nc={optional=true}\n[features]\nb=[\"c\"]" - .to_owned(), - true, - Path::new(""), - &["c".to_owned()] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![]), - }) - ); - assert_eq!( - Manifest::from_toml( - "[package]\nname=\"\"\n[dependencies]\nc={optional=true}\n[features]\nb=[\"c\"]" - .to_owned(), - true, - Path::new(""), - &["b".to_owned()] - ), - Ok(Manifest { - package: Package { - msrv: None, - name: String::new(), - }, - features: Features(vec![("c".to_owned(), Vec::new())]), - }) - ); - } - #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] - #[expect( - clippy::cognitive_complexity, - clippy::too_many_lines, - reason = "want to test for a lot of things" - )] - #[test] - fn power_set() { - #[cfg(target_pointer_width = "16")] - let feat_len_one_too_large = 17; - #[cfg(target_pointer_width = "32")] - let feat_len_one_too_large = 33; - #[cfg(target_pointer_width = "64")] - let feat_len_one_too_large = 65; - let mut feats = Features(vec![(String::new(), Vec::new()); feat_len_one_too_large]); - assert_eq!(PowerSet::new(&feats, false), Err(TooManyFeaturesErr)); - #[cfg(target_pointer_width = "16")] - let max_feat_len = 16; - #[cfg(target_pointer_width = "32")] - let max_feat_len = 32; - #[cfg(target_pointer_width = "64")] - let max_feat_len = 64; - feats.0 = vec![(String::new(), Vec::new()); max_feat_len]; - #[cfg(any( - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64" - ))] - assert_eq!( - PowerSet::new(&feats, false), - Ok(Some(PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: usize::MAX, - buffer: vec![""; max_feat_len], - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false, - })) - ); - feats.0 = Vec::new(); - assert_eq!( - PowerSet::new(&feats, false), - Ok(Some(PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false, - })) - ); - assert_eq!(PowerSet::new(&feats, true), Ok(None)); - let mut power_set = PowerSet::new(&feats, false) - .unwrap_or_else(|_e| { - unreachable!("not possible since we just verified PowerSet::new returned Ok") - }) - .unwrap_or_else(|| { - unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") - }); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); - assert_eq!(power_set.next_set(), Some("")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - power_set.reset(); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - // [features] - // a = ["b"] - // b = ["c", "d"] - // c = [] - // d = [] - feats.0 = vec![ - ("a".to_owned(), vec!["b".to_owned()]), - ("b".to_owned(), vec!["c".to_owned(), "d".to_owned()]), - ("c".to_owned(), Vec::new()), - ("d".to_owned(), Vec::new()), - ]; - assert_eq!( - PowerSet::new(&feats, false), - Ok(Some(PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - // At least one feature depends on another, so this will be set to `true`. - check_overlap: true, - idx: 15, - buffer: vec!["a", "b", "c", "d"], - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - })) - ); - power_set = PowerSet::new(&feats, false) - .unwrap_or_else(|_e| { - unreachable!("not possible since we just verified PowerSet::new returned Ok") - }) - .unwrap_or_else(|| { - unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") - }); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); - // Order is the following: - // 1. a,b,c,d: skipped since a depends on b. - // 2. b,c,d: skipped since b depends on c. - // 3. a,c,d: skipped since a depends on c (via b). - // 4. c,d - // 5. a,b,d: skipped since a depends on b. - // 6. b,d: skipped since b depends on d. - // 7. a,d: skipped since a depends on d (via b). - // 8. d - // 9. a,b,c: skipped since a depends on b. - // 10. b,c: skipped since b depends on c. - // 11. a,c: skipped since a depends on c (via b). - // 12. c - // 13. a,b: skipped since a depends on b. - // 14. b - // 15. a - // 16. - assert_eq!(power_set.next_set(), Some("c,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: true, - // We started at 15, and we iterated 4 items (skipping 3). - idx: 11, - buffer: vec!["c", "d"], - set: "c,d".to_owned(), - skipped_sets_counter: 3, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: true, - // We started at 11, and we iterated 4 items (skipping 3). - idx: 7, - buffer: vec!["d"], - set: "d".to_owned(), - skipped_sets_counter: 6, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("c")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: true, - // We started at 7, and we iterated 4 items (skipping 3). - idx: 3, - buffer: vec!["c"], - set: "c".to_owned(), - skipped_sets_counter: 9, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("b")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: true, - // We started at 3, and we iterated 2 items (skipping 1). - idx: 1, - buffer: vec!["b"], - set: "b".to_owned(), - skipped_sets_counter: 10, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: true, - // We started at 1, and we iterated 1 item. - idx: 0, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 10, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: true, - // We started at 0, and we iterated 1 item but we don't underflow instead `has_remaining` is set - // to `false`. - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 10, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - // Internal state is left unchanged. - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: true, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 10, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - // Internal state is left unchanged. - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: true, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 10, - skip_empty_set: false - } - ); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); - power_set.reset(); - // `PowerSet::reset` only resets what is necessary nothing more; in particular, `buffer` and `set` are - // left alone. - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: true, - idx: 15, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); - // Same as above except no feature depends on any other. - // [features] - // a = [] - // b = [] - // c = [] - // d = [] - feats.0 = vec![ - ("a".to_owned(), Vec::new()), - ("b".to_owned(), Vec::new()), - ("c".to_owned(), Vec::new()), - ("d".to_owned(), Vec::new()), - ]; - assert_eq!( - PowerSet::new(&feats, false), - Ok(Some(PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 15, - buffer: vec!["a", "b", "c", "d"], - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - })) - ); - power_set = PowerSet::new(&feats, false) - .unwrap_or_else(|_e| { - unreachable!("not possible since we just verified PowerSet::new returned Ok") - }) - .unwrap_or_else(|| { - unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") - }); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); - // Order is the same as above except nothing is skipped: - // 1. a,b,c,d - // 2. b,c,d - // 3. a,c,d - // 4. c,d - // 5. a,b,d - // 6. b,d - // 7. a,d - // 8. d - // 9. a,b,c - // 10. b,c - // 11. a,c - // 12. c - // 13. a,b - // 14. b - // 15. a - // 16. - assert_eq!(power_set.next_set(), Some("a,b,c,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 14, - buffer: vec!["a", "b", "c", "d"], - set: "a,b,c,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("b,c,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 13, - buffer: vec!["b", "c", "d"], - set: "b,c,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a,c,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 12, - buffer: vec!["a", "c", "d"], - set: "a,c,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("c,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 11, - buffer: vec!["c", "d"], - set: "c,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a,b,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 10, - buffer: vec!["a", "b", "d"], - set: "a,b,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("b,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 9, - buffer: vec!["b", "d"], - set: "b,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a,d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 8, - buffer: vec!["a", "d"], - set: "a,d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("d")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 7, - buffer: vec!["d"], - set: "d".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a,b,c")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 6, - buffer: vec!["a", "b", "c"], - set: "a,b,c".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("b,c")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 5, - buffer: vec!["b", "c"], - set: "b,c".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a,c")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 4, - buffer: vec!["a", "c"], - set: "a,c".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("c")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 3, - buffer: vec!["c"], - set: "c".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a,b")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 2, - buffer: vec!["a", "b"], - set: "a,b".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("b")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 1, - buffer: vec!["b"], - set: "b".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("a")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 0, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), Some("")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.next_set(), None); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: Vec::new(), - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: false - } - ); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); - feats.0 = vec![("a".to_owned(), Vec::new())]; - assert_eq!( - PowerSet::new(&feats, true), - Ok(Some(PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 1, - buffer: vec!["a"], - set: String::new(), - skipped_sets_counter: 0, - skip_empty_set: true - })) - ); - power_set = PowerSet::new(&feats, true) - .unwrap_or_else(|_e| { - unreachable!("not possible since we just verified PowerSet::new returned Ok") - }) - .unwrap_or_else(|| { - unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") - }); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); - assert_eq!(power_set.next_set(), Some("a")); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: true, - } - ); - assert_eq!(power_set.next_set(), None); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: true, - } - ); - assert_eq!(power_set.next_set(), None); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: true, - } - ); - power_set.reset(); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: true, - check_overlap: false, - idx: 1, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: true, - } - ); - assert_eq!(power_set.next_set(), Some("a")); - assert_eq!(power_set.next_set(), None); - assert_eq!( - power_set, - PowerSet { - feats: feats.0.as_slice(), - has_remaining: false, - check_overlap: false, - idx: 0, - buffer: vec!["a"], - set: "a".to_owned(), - skipped_sets_counter: 0, - skip_empty_set: true, - } - ); - assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); - } -} diff --git a/src/manifest/tests.rs b/src/manifest/tests.rs @@ -0,0 +1,1950 @@ +use super::{ + DependenciesErr, FeatureDependenciesErr, Features, FeaturesErr, ImpliedFeaturesErr, Manifest, + ManifestErr, Msrv, NonZeroUsizePlus1, Package, PackageErr, Path, PathBuf, PowerSet, + TooManyFeaturesErr, TryLockError, WorkspaceErr, +}; +impl PartialEq for PackageErr { + #[expect(clippy::cognitive_complexity, reason = "long match expression")] + fn eq(&self, other: &Self) -> bool { + match *self { + Self::Missing => matches!(*other, Self::Missing), + Self::InvalidType => matches!(*other, Self::InvalidType), + Self::MissingName => matches!(*other, Self::MissingName), + Self::InvalidNameType => matches!(*other, Self::InvalidNameType), + Self::InvalidMsrvType => matches!(*other, Self::InvalidMsrvType), + Self::Msrv => matches!(*other, Self::Msrv), + Self::MsrvWorkspaceMissing => matches!(*other, Self::MsrvWorkspaceMissing), + Self::MsrvWorkspaceVal => matches!(*other, Self::MsrvWorkspaceVal), + Self::InvalidWorkspaceType => matches!(*other, Self::InvalidWorkspaceType), + Self::WorkspaceIo(ref e) => { + matches!(*other, Self::WorkspaceIo(ref e2) if e.kind() == e2.kind()) + } + Self::WorkspaceLock(ref e) => { + matches!(*other, Self::WorkspaceLock(ref e2) if match *e { + TryLockError::Error(ref inner_e) => matches!(*e2, TryLockError::Error(ref inner_e2) if inner_e.kind() == inner_e2.kind()), + TryLockError::WouldBlock => matches!(*e2, TryLockError::WouldBlock), + }) + } + Self::WorkspaceLenMismatch => matches!(*other, Self::WorkspaceLenMismatch), + Self::WorkspaceDoesNotExist => matches!(*other, Self::WorkspaceDoesNotExist), + Self::WorkspaceRead(ref e, ref p) => { + matches!(*other, Self::WorkspaceRead(ref e2, ref p2) if e.kind() == e2.kind() && p == p2) + } + Self::WorkspaceReadLock(ref e, ref p) => { + matches!(*other, Self::WorkspaceReadLock(ref e2, ref p2) if p == p2 && match *e { + TryLockError::Error(ref inner_e) => matches!(*e2, TryLockError::Error(ref inner_e2) if inner_e.kind() == inner_e2.kind()), + TryLockError::WouldBlock => matches!(*e2, TryLockError::WouldBlock), + }) + } + Self::WorkspaceReadLenMismatch(ref p) => { + matches!(*other, Self::WorkspaceReadLenMismatch(ref p2) if p == p2) + } + Self::WorkspaceToml(ref e, ref p) => { + matches!(*other, Self::WorkspaceToml(ref e2, ref p2) if e == e2 && p == p2) + } + Self::Workspace(e, ref p) => { + matches!(*other, Self::Workspace(e2, ref p2) if e == e2 && p == p2) + } + } + } +} +#[expect( + clippy::cognitive_complexity, + clippy::too_many_lines, + reason = "want to test a lot of things" +)] +#[test] +fn cargo_toml() { + assert!( + Manifest::from_toml("a".to_owned(), false, Path::new(""), &[]) + .map_or_else(|e| matches!(*e, ManifestErr::Toml(_, _)), |_| false) + ); + assert_eq!( + Manifest::from_toml(String::new(), false, Path::new(""), &[]), + Err(Box::new(ManifestErr::Package( + PackageErr::Missing, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml("[' package']".to_owned(), false, Path::new(""), &[]), + Err(Box::new(ManifestErr::Package( + PackageErr::Missing, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml("['package ']".to_owned(), false, Path::new(""), &[]), + Err(Box::new(ManifestErr::Package( + PackageErr::Missing, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml("package=2".to_owned(), false, Path::new(""), &[]), + Err(Box::new(ManifestErr::Package( + PackageErr::InvalidType, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml("[package]".to_owned(), false, Path::new(""), &[]), + Err(Box::new(ManifestErr::Package( + PackageErr::MissingName, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml("[package]\nname=true".to_owned(), false, Path::new(""), &[]), + Err(Box::new(ManifestErr::Package( + PackageErr::InvalidNameType, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n\nrust-version=2".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::InvalidMsrvType, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"a\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"1.00.0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"1..0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"1.\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"01.0.0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"1.0.0.1\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"111111111111111111111111.2.3\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"1.0.0-nightly\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"-1.0.0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\" 1.0.0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"1.0.0 \"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Msrv, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version={}".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::MsrvWorkspaceMissing, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version={workspace=2}".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::MsrvWorkspaceVal, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version={workspace=false}".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::MsrvWorkspaceVal, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version={workspace=true}\nworkspace=2".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::InvalidWorkspaceType, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "workspace=2\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Workspace(WorkspaceErr::InvalidType, PathBuf::new()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[workspace]\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Workspace(WorkspaceErr::MissingPackage, PathBuf::new()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[workspace]\npackage=2\n[package]\nname=\"\"\nrust-version={workspace=true}" + .to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Workspace(WorkspaceErr::InvalidPackageType, PathBuf::new()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[workspace.package]\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Workspace(WorkspaceErr::MissingPackageMsrv, PathBuf::new()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[workspace.package]\nrust-version={}\n[package]\nname=\"\"\nrust-version={workspace=true}" + .to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Workspace(WorkspaceErr::InvalidPackageMsrvType, PathBuf::new()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[workspace.package]\nrust-version=\"\"\n[package]\nname=\"\"\nrust-version={workspace=true}" + .to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Package( + PackageErr::Workspace(WorkspaceErr::Msrv, PathBuf::new()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "features=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::InvalidType, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"/\"=[]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::InvalidName("/".to_owned()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"dep:\"=[]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::InvalidName("dep:".to_owned()), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidFeatureType( + String::new() + )), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[true]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependencyType( + String::new() + )), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( + String::new(), + "foo".to_owned() + )), + PathBuf::new() + ))) + ); + // Feature dependencies can't be implied features when implied features are forbidden. + assert_eq!( + Manifest::from_toml( + "[dependencies]\nfoo={optional=true}\n[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"" + .to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( + String::new(), + "foo".to_owned() + )), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"\"]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature(String::new())), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"a\"]\na=[\"\"]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature(String::new())), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"a\"]\na=[\"b\"]\nb=[\"a\"]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature("a".to_owned())), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"a\"]\na=[\"c\",\"b\"]\nb=[\"a\"]\nc=[]\n[package]\nname=\"\"" + .to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature("a".to_owned())), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[]\na=[\"c\",\"b\"]\nb=[\"a\"]\nc=[]\n[package]\nname=\"\"" + .to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature("a".to_owned())), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"a\",\"b\"]\na=[\"b\"]\nb=[]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( + String::new(), + "b".to_owned() + )), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"a\",\"a\"]\na=[]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( + String::new(), + "a".to_owned() + )), + PathBuf::new() + ))) + ); + // Duplicate `"dep:"` feature dependencies error. + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"dep:\",\"dep:\"]\na=[]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( + String::new(), + "dep:".to_owned() + )), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "target=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TargetType, + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "dependencies=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::Type("dependencies")), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "build-dependencies=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::Type("build-dependencies")), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( + "dependencies", + "dep:".to_owned() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::Name("dependencies", "/".to_owned())), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[build-dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( + "build-dependencies", + "dep:".to_owned() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[build-dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( + "build-dependencies", + "/".to_owned() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[dependencies]\n\"\"=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::DependencyType( + "dependencies", + String::new() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[build-dependencies]\n\"\"=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::DependencyType( + "build-dependencies", + String::new() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::OptionalType( + "dependencies", + String::new() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[build-dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::OptionalType( + "build-dependencies", + String::new() + )), + PathBuf::new(), + ))) + ); + // Implied features are disallowed iff `!allow_implied_features`. + assert_eq!( + Manifest::from_toml( + "[dependencies]\nfoo={optional=true}\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::Dependencies(DependenciesErr::ImpliedFeature( + "dependencies", + "foo".to_owned() + )), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target]\n\"\"=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TargetPlatformType(String::new()), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\"]\ndependencies=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::Type("dependencies") + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\"]\nbuild-dependencies=2\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::Type("build-dependencies") + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::Name("dependencies", "/".to_owned()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::Name("dependencies", "dep:".to_owned()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".build-dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::Name("build-dependencies", "/".to_owned()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".build-dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::Name("build-dependencies", "dep:".to_owned()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".dependencies]\n\"\"=false\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::DependencyType("dependencies", String::new()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".build-dependencies]\n\"\"=false\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::DependencyType("build-dependencies", String::new()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::OptionalType("dependencies", String::new()) + ), + PathBuf::new(), + ))) + ); + assert_eq!( + Manifest::from_toml( + "[target.\"\".build-dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::TagetPlatformDependencies( + String::new(), + DependenciesErr::OptionalType("build-dependencies", String::new()) + ), + PathBuf::new(), + ))) + ); + // An invalid dependency error occurs later when we `allow_implied_features` since + // implied features aren't added until after feature extraction. + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), + true, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::ImpliedFeatures( + ImpliedFeaturesErr::InvalidDependency(String::new(), "foo".to_owned()), + PathBuf::new(), + ))) + ); + // In contrast, above would have erred sooner if `!allow_implied_features`. + assert_eq!( + Manifest::from_toml( + "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &[] + ), + Err(Box::new(ManifestErr::Features( + FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( + String::new(), + "foo".to_owned() + )), + PathBuf::new() + ))) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"".to_owned(), + false, + Path::new(""), + &["a".to_owned()] + ), + Err(Box::new(ManifestErr::UndefinedIgnoreFeature( + "a".to_owned(), + PathBuf::new() + ))) + ); + // Even if we forbid implied features, we don't error when a feature is defined + // with the same name of an implied feature. This is due to simplicity in code + // and the fact that `cargo` will error anyway. + // + // For example once `cargo` is invoked, an error will occur due to duplicate features: + // the explict feature `foo` and the implied feature from the dependency `foo`. + assert_eq!( + Manifest::from_toml( + "[dependencies]\nfoo={optional=true}\n[features]\nfoo=[]\n[package]\nname=\"\"" + .to_owned(), + false, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![("foo".to_owned(), Vec::new())]), + }) + ); + // Allow major-only MSRV. + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"foo\"\nrust-version=\"0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: Some(Msrv { + major: 0, + minor: None, + patch: None, + }), + name: "foo".to_owned(), + }, + features: Features(Vec::new()), + }) + ); + // Allow escapes. + assert_eq!( + Manifest::from_toml( + "[\"\\u0070ackage\"]\n\"n\\u0061me\"=\"\\u0066oo\"\n\"\\u0072ust-version\"=\"0\\u002E\\u0031\"".to_owned(), + false, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: Some(Msrv { + major: 0, + minor: Some(1), + patch: None, + }), + name: "foo".to_owned(), + }, + features: Features(Vec::new()), + }) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\nrust-version=\"0.0.0\"".to_owned(), + false, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: Some(Msrv { + major: 0, + minor: Some(0), + patch: Some(0), + }), + name: String::new(), + }, + features: Features(Vec::new()), + }) + ); + // Ignore non `rust-version` keys in `package`. Ignore keys in the root document except `package`, + // `features`, `dependencies`, `build-dependencies`, and `target`. Ignore keys in + // `target.<something>` unless the key is `dependencies` or `build-dependencies`. Don't treat + // `<something>` special in `target.<something>` other than its being a table. + assert_eq!( + Manifest::from_toml("dev-dependencies=2\n[package]\nname=\"\"\n\nfoo=2\nrust-version=\"18446744073709551615.18446744073709551615.18446744073709551615\"\n[foo]\nbar=false\n[target.\"\".foo]\nbar=2\n[target.foo]\nbar=false\n[target.dependencies]\nfoo=2\n[target.build-dependencies]\nfoo=false\n[target.dev-dependencies]\nfoo=true\n".to_owned(), false, Path::new(""), &[]), + Ok(Manifest { + package: Package { + msrv: Some(Msrv { + major: u64::MAX, + minor: Some(u64::MAX), + patch: Some(u64::MAX), + }), + name: String::new(), + }, + features: Features(Vec::new()), + }) + ); + // [package] + // name = "" + // + // ["\u0064ependencies"] + // "\u0000" = "\u0000" + // a = { optional = true } + // + // ["build-\u0064ependencies"] + // "\u0000" = { optional = true } + // + // [dev-dependencies] + // buzz = { optional = true } + // + // [target."".dependencies] + // b = { optional = false, foo = 2 } + // fizz = { optional = true, foo = 3 } + // + // [target.a.dependencies] + // c = { optional = true } + // wuzz = { optional = true } + // + // [features] + // default = ["bar","dep:lk","a/ak", "a/ak"] + // bar = ["dep\u003Awuzz"] + // + // We allow any and all key names unless it's the features table or a dependency table; in which case + // key names must not contain `'/'` nor begin with `"dep:"`. + // + // The order of features is based on the following hierarchy: + // * Explict features: lexicographically sorted + // * dependencies: optional only, lexicographically sorted, only if an explicit feature doesn't exist with + // the same name nor any explicit feature contains a dependency named `"dep:<dependecy>"` and we allow + // implied features. If such feature exists, we don't error but simply don't add. + // * build-dependencies: read above. + // * target.<something>: lexicographically sorted by something, within `something`, `dependencies` + // is first using the same methodology as item 2, last `build-dependencies`. + // + // Once the order of features is determined, the only feature dependencies that are retained are those + // that don't contain `'/'` nor begin with `"dep:"`. We don't require dependencies to be defined for + // feature dependencies that contain `'/'` or begin with `"dep:"`. We don't care about duplicate feature + // dependencies that contain `'/'`. + // + // Based on above, `Features` looks like the following: + // 1. (bar, []) + // 2. (default, ["bar"]) + // 3. (a, []) + // 4. (\x00, []) + // 5. (fizz, []) + // 6. (c, []) + assert_eq!( + Manifest::from_toml( + "[\"\\u0064ependencies\"]\n\"\\u0000\"=\"\\u0000\"\na={optional=true}\n[\"build-\\u0064ependencies\"]\n\"\\u0000\"={optional=true}\n[target.\"\".dependencies]\nb={optional=false,foo=2}\nfizz={optional=true,foo=3}\n[features]\ndefault=[\"bar\",\"dep:lk\",\"a/ak\",\"a/ak\"]\nbar=[\"dep\\u003Awuzz\"]\n[dev-dependencies]\nbuzz={optional=true}\n[target.a.dependencies]\nc={optional=true}\nwuzz={optional=true}\n[package]\nname=\"\"".to_owned(), + true, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![("bar".to_owned(), Vec::new()), ("default".to_owned(), vec!["bar".to_owned()]), ("a".to_owned(), Vec::new()), ("\0".to_owned(), Vec::new()), ("fizz".to_owned(), Vec::new()), ("c".to_owned(), Vec::new())]), + }) + ); + // [package] + // name = "" + // + // [dependencies] + // foo = { "optional" = true } + // fizz = { "optional" = true } + // + // [features] + // fizz = ["dep:fizz"] + // bar = ["dep:foo"] + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n[dependencies]\nfoo={optional=true}\nfizz={optional=true}\n[features]\nfizz=[\"dep:fizz\"]\nbar=[\"dep:foo\"]".to_owned(), + false, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![ + ("bar".to_owned(), Vec::new()), + ("fizz".to_owned(), Vec::new()) + ]), + }) + ); + // [package] + // name = "" + // + // [dependencies] + // bar = { "optional" = true } + // + // [features] + // foo = ["bar"] + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n[dependencies]\nbar={optional=true}\n[features]\nfoo=[\"bar\"]" + .to_owned(), + true, + Path::new(""), + &[] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![ + ("foo".to_owned(), vec!["bar".to_owned()]), + ("bar".to_owned(), Vec::new()), + ]), + }) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n[features]\na=[]\nb=[\"a\"]".to_owned(), + false, + Path::new(""), + &["a".to_owned()] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![]), + }) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n[features]\na=[]\nb=[\"a\"]".to_owned(), + false, + Path::new(""), + &["b".to_owned()] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![("a".to_owned(), Vec::new())]), + }) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n[dependencies]\nc={optional=true}\n[features]\nb=[\"c\"]" + .to_owned(), + true, + Path::new(""), + &["c".to_owned()] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![]), + }) + ); + assert_eq!( + Manifest::from_toml( + "[package]\nname=\"\"\n[dependencies]\nc={optional=true}\n[features]\nb=[\"c\"]" + .to_owned(), + true, + Path::new(""), + &["b".to_owned()] + ), + Ok(Manifest { + package: Package { + msrv: None, + name: String::new(), + }, + features: Features(vec![("c".to_owned(), Vec::new())]), + }) + ); +} +#[expect(clippy::unreachable, reason = "want to crash when there is a bug")] +#[expect( + clippy::cognitive_complexity, + clippy::too_many_lines, + reason = "want to test for a lot of things" +)] +#[test] +fn power_set() { + #[cfg(target_pointer_width = "16")] + let feat_len_one_too_large = 17; + #[cfg(target_pointer_width = "32")] + let feat_len_one_too_large = 33; + #[cfg(target_pointer_width = "64")] + let feat_len_one_too_large = 65; + let mut feats = Features(vec![(String::new(), Vec::new()); feat_len_one_too_large]); + assert_eq!(PowerSet::new(&feats, false), Err(TooManyFeaturesErr)); + #[cfg(target_pointer_width = "16")] + let max_feat_len = 16; + #[cfg(target_pointer_width = "32")] + let max_feat_len = 32; + #[cfg(target_pointer_width = "64")] + let max_feat_len = 64; + feats.0 = vec![(String::new(), Vec::new()); max_feat_len]; + #[cfg(any( + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64" + ))] + assert_eq!( + PowerSet::new(&feats, false), + Ok(Some(PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: usize::MAX, + buffer: vec![""; max_feat_len], + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false, + })) + ); + feats.0 = Vec::new(); + assert_eq!( + PowerSet::new(&feats, false), + Ok(Some(PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false, + })) + ); + assert_eq!(PowerSet::new(&feats, true), Ok(None)); + let mut power_set = PowerSet::new(&feats, false) + .unwrap_or_else(|_e| { + unreachable!("not possible since we just verified PowerSet::new returned Ok") + }) + .unwrap_or_else(|| { + unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") + }); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); + assert_eq!(power_set.next_set(), Some("")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + power_set.reset(); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + // [features] + // a = ["b"] + // b = ["c", "d"] + // c = [] + // d = [] + feats.0 = vec![ + ("a".to_owned(), vec!["b".to_owned()]), + ("b".to_owned(), vec!["c".to_owned(), "d".to_owned()]), + ("c".to_owned(), Vec::new()), + ("d".to_owned(), Vec::new()), + ]; + assert_eq!( + PowerSet::new(&feats, false), + Ok(Some(PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + // At least one feature depends on another, so this will be set to `true`. + check_overlap: true, + idx: 15, + buffer: vec!["a", "b", "c", "d"], + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + })) + ); + power_set = PowerSet::new(&feats, false) + .unwrap_or_else(|_e| { + unreachable!("not possible since we just verified PowerSet::new returned Ok") + }) + .unwrap_or_else(|| { + unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") + }); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); + // Order is the following: + // 1. a,b,c,d: skipped since a depends on b. + // 2. b,c,d: skipped since b depends on c. + // 3. a,c,d: skipped since a depends on c (via b). + // 4. c,d + // 5. a,b,d: skipped since a depends on b. + // 6. b,d: skipped since b depends on d. + // 7. a,d: skipped since a depends on d (via b). + // 8. d + // 9. a,b,c: skipped since a depends on b. + // 10. b,c: skipped since b depends on c. + // 11. a,c: skipped since a depends on c (via b). + // 12. c + // 13. a,b: skipped since a depends on b. + // 14. b + // 15. a + // 16. + assert_eq!(power_set.next_set(), Some("c,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: true, + // We started at 15, and we iterated 4 items (skipping 3). + idx: 11, + buffer: vec!["c", "d"], + set: "c,d".to_owned(), + skipped_sets_counter: 3, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: true, + // We started at 11, and we iterated 4 items (skipping 3). + idx: 7, + buffer: vec!["d"], + set: "d".to_owned(), + skipped_sets_counter: 6, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("c")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: true, + // We started at 7, and we iterated 4 items (skipping 3). + idx: 3, + buffer: vec!["c"], + set: "c".to_owned(), + skipped_sets_counter: 9, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("b")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: true, + // We started at 3, and we iterated 2 items (skipping 1). + idx: 1, + buffer: vec!["b"], + set: "b".to_owned(), + skipped_sets_counter: 10, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: true, + // We started at 1, and we iterated 1 item. + idx: 0, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 10, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: true, + // We started at 0, and we iterated 1 item but we don't underflow instead `has_remaining` is set + // to `false`. + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 10, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + // Internal state is left unchanged. + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: true, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 10, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + // Internal state is left unchanged. + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: true, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 10, + skip_empty_set: false + } + ); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); + power_set.reset(); + // `PowerSet::reset` only resets what is necessary nothing more; in particular, `buffer` and `set` are + // left alone. + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: true, + idx: 15, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); + // Same as above except no feature depends on any other. + // [features] + // a = [] + // b = [] + // c = [] + // d = [] + feats.0 = vec![ + ("a".to_owned(), Vec::new()), + ("b".to_owned(), Vec::new()), + ("c".to_owned(), Vec::new()), + ("d".to_owned(), Vec::new()), + ]; + assert_eq!( + PowerSet::new(&feats, false), + Ok(Some(PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 15, + buffer: vec!["a", "b", "c", "d"], + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + })) + ); + power_set = PowerSet::new(&feats, false) + .unwrap_or_else(|_e| { + unreachable!("not possible since we just verified PowerSet::new returned Ok") + }) + .unwrap_or_else(|| { + unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") + }); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); + // Order is the same as above except nothing is skipped: + // 1. a,b,c,d + // 2. b,c,d + // 3. a,c,d + // 4. c,d + // 5. a,b,d + // 6. b,d + // 7. a,d + // 8. d + // 9. a,b,c + // 10. b,c + // 11. a,c + // 12. c + // 13. a,b + // 14. b + // 15. a + // 16. + assert_eq!(power_set.next_set(), Some("a,b,c,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 14, + buffer: vec!["a", "b", "c", "d"], + set: "a,b,c,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("b,c,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 13, + buffer: vec!["b", "c", "d"], + set: "b,c,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a,c,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 12, + buffer: vec!["a", "c", "d"], + set: "a,c,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("c,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 11, + buffer: vec!["c", "d"], + set: "c,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a,b,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 10, + buffer: vec!["a", "b", "d"], + set: "a,b,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("b,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 9, + buffer: vec!["b", "d"], + set: "b,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a,d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 8, + buffer: vec!["a", "d"], + set: "a,d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("d")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 7, + buffer: vec!["d"], + set: "d".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a,b,c")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 6, + buffer: vec!["a", "b", "c"], + set: "a,b,c".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("b,c")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 5, + buffer: vec!["b", "c"], + set: "b,c".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a,c")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 4, + buffer: vec!["a", "c"], + set: "a,c".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("c")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 3, + buffer: vec!["c"], + set: "c".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a,b")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 2, + buffer: vec!["a", "b"], + set: "a,b".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("b")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 1, + buffer: vec!["b"], + set: "b".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("a")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 0, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), Some("")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.next_set(), None); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: Vec::new(), + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: false + } + ); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); + feats.0 = vec![("a".to_owned(), Vec::new())]; + assert_eq!( + PowerSet::new(&feats, true), + Ok(Some(PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 1, + buffer: vec!["a"], + set: String::new(), + skipped_sets_counter: 0, + skip_empty_set: true + })) + ); + power_set = PowerSet::new(&feats, true) + .unwrap_or_else(|_e| { + unreachable!("not possible since we just verified PowerSet::new returned Ok") + }) + .unwrap_or_else(|| { + unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") + }); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); + assert_eq!(power_set.next_set(), Some("a")); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: true, + } + ); + assert_eq!(power_set.next_set(), None); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: true, + } + ); + assert_eq!(power_set.next_set(), None); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: true, + } + ); + power_set.reset(); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: true, + check_overlap: false, + idx: 1, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: true, + } + ); + assert_eq!(power_set.next_set(), Some("a")); + assert_eq!(power_set.next_set(), None); + assert_eq!( + power_set, + PowerSet { + feats: feats.0.as_slice(), + has_remaining: false, + check_overlap: false, + idx: 0, + buffer: vec!["a"], + set: "a".to_owned(), + skipped_sets_counter: 0, + skip_empty_set: true, + } + ); + assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); +}