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:
| M | Cargo.toml | | | 25 | +++++++++++++++++++------ |
| M | src/args.rs | | | 823 | +------------------------------------------------------------------------------ |
| A | src/args/tests.rs | | | 817 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/cargo.rs | | | 77 | +++-------------------------------------------------------------------------- |
| A | src/cargo/tests.rs | | | 71 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/main.rs | | | 3 | ++- |
| M | src/manifest.rs | | | 1971 | +------------------------------------------------------------------------------ |
| A | src/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));
+}