manifest.rs (157834B)
1 use super::{ 2 args::NonZeroUsizePlus1, 3 cargo::{Toolchain, ToolchainErr}, 4 }; 5 use alloc::borrow::Cow; 6 use core::cmp::Ordering; 7 use std::{ 8 fs::{File, TryLockError}, 9 io::{Error, ErrorKind, Read as _, StderrLock, Write as _}, 10 path::{Path, PathBuf}, 11 }; 12 use toml::{ 13 Spanned, 14 de::{DeArray, DeValue, Error as TomlErr}, 15 map::Map, 16 }; 17 /// `"workspace"`. 18 const WORKSPACE: &str = "workspace"; 19 /// `"package"`. 20 const PACKAGE: &str = "package"; 21 /// `"rust-version"`. 22 const RUST_VERSION: &str = "rust-version"; 23 /// Error returned from extracting `"workspace"`. 24 #[cfg_attr(test, derive(Debug, PartialEq))] 25 #[derive(Clone, Copy)] 26 pub(crate) enum WorkspaceErr { 27 /// Variant returned when there is no `"workspace"` key. 28 Missing, 29 /// Variant returned when `"workspace"` is not a table. 30 InvalidType, 31 /// Variant returned when `workspace.package` does not exist. 32 MissingPackage, 33 /// Variant returned when `workspace.package` is not a table. 34 InvalidPackageType, 35 /// Variant returned when `workspace.package.rust-version` does not exist. 36 MissingPackageMsrv, 37 /// Variant returned when `workspace.package.rust-version` is not a string. 38 InvalidPackageMsrvType, 39 /// Variant returned when `workspace.package.rust-version` is not a valid MSRV. 40 Msrv, 41 } 42 impl WorkspaceErr { 43 /// Writes `self` to `stderr`. 44 fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> { 45 match self { 46 Self::Missing => writeln!( 47 stderr, 48 "'{WORKSPACE}' does not exist in {}.", 49 file.display() 50 ), 51 Self::InvalidType => writeln!( 52 stderr, 53 "'{WORKSPACE}' exists but is not a table in {}.", 54 file.display() 55 ), 56 Self::MissingPackage => writeln!( 57 stderr, 58 "'{WORKSPACE}.{PACKAGE}' does not exist in {}.", 59 file.display() 60 ), 61 Self::InvalidPackageType => writeln!( 62 stderr, 63 "'{WORKSPACE}.{PACKAGE}' exists but is not a table in {}.", 64 file.display() 65 ), 66 Self::MissingPackageMsrv => writeln!( 67 stderr, 68 "'{WORKSPACE}.{PACKAGE}.{RUST_VERSION}' does not exist in {}.", 69 file.display() 70 ), 71 Self::InvalidPackageMsrvType => writeln!( 72 stderr, 73 "'{WORKSPACE}.{PACKAGE}.{RUST_VERSION}' exists but is not a string in {}.", 74 file.display() 75 ), 76 Self::Msrv => writeln!( 77 stderr, 78 "'{WORKSPACE}.{PACKAGE}.{RUST_VERSION}' exists but is not a valid MSRV in {}.", 79 file.display() 80 ), 81 } 82 } 83 } 84 /// `"name"`. 85 const NAME: &str = "name"; 86 /// Error returned from extracting `"package"`. 87 #[cfg_attr(test, derive(Debug))] 88 pub(crate) enum PackageErr { 89 /// Variant returned when there is no `"package"` key. 90 Missing, 91 /// Variant returned when `"package"` is not a table. 92 InvalidType, 93 /// Variant returned when `packagen.name` does not exist. 94 MissingName, 95 /// Variant returned when `package.name` is not a string. 96 InvalidNameType, 97 /// Variant returned when `package.rust-version` is not a string nor table. 98 InvalidMsrvType, 99 /// Variant returned when `package.rust-version` is not a valid MSRV. 100 Msrv, 101 /// Variant returned when `package.rust-version` is table that doesn't contain the `"workspace"` key. 102 MsrvWorkspaceMissing, 103 /// Variant returned when `package.rust-version.workspace` is not a Boolean with value `true`. 104 MsrvWorkspaceVal, 105 /// Variant returned when `package.workspace` is not a string. 106 InvalidWorkspaceType, 107 /// Variant returned when searching for the workspace file errors. 108 WorkspaceIo(Error), 109 /// Variant when locking the workspace file errors. 110 WorkspaceLock(TryLockError), 111 /// Variant when the length of the workspace file does not match the length reported from the file system. 112 WorkspaceLenMismatch, 113 /// Variant returned when there is no workspace `Cargo.toml`. 114 /// 115 /// This is only returned if the package's MSRV is inherited from the workspace, there is no 116 /// `workspace` key in the package's `Cargo.toml`, and there is no `workspace` key in the table 117 /// `package` (i.e., this is only returned when we must search for it by crawling up the directory). 118 WorkspaceDoesNotExist, 119 /// Variant returned when the file located at `package.workspace` could not be read. 120 /// 121 /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched 122 /// for the workspace and found a `Cargo.toml`. 123 WorkspaceRead(Error, PathBuf), 124 /// Variant returned when the file located at `package.workspace` could not be locked. 125 /// 126 /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched 127 /// for the workspace and found a `Cargo.toml`. 128 WorkspaceReadLock(TryLockError, PathBuf), 129 /// Variant returned when the length of the file located at `package.workspace` does not match the length 130 /// reported by the file system. 131 /// 132 /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched 133 /// for the workspace and found a `Cargo.toml`. 134 WorkspaceReadLenMismatch(PathBuf), 135 /// Variant returned when the file located at `package.workspace` is not valid TOML. 136 /// 137 /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched 138 /// for the workspace and found a `Cargo.toml`. 139 WorkspaceToml(TomlErr, PathBuf), 140 /// Variant returned when `package.rust-version` defers to the workspace MSRV, but there was an error 141 /// extracting the workspace MSRV from the file located at the contained `PathBuf`. 142 Workspace(WorkspaceErr, PathBuf), 143 } 144 impl PackageErr { 145 /// Writes `self` to `stderr`. 146 #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] 147 fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> { 148 match self { 149 Self::Missing => writeln!(stderr, "'{PACKAGE}' does not exist in {}.", file.display()), 150 Self::InvalidType => writeln!( 151 stderr, 152 "'{PACKAGE}' exists but is not a table in {}.", 153 file.display() 154 ), 155 Self::MissingName => writeln!(stderr, "'{PACKAGE}.{NAME}' does not exist in {}.", file.display()), 156 Self::InvalidNameType => writeln!( 157 stderr, 158 "'{PACKAGE}.{NAME}' exists but is not a string in {}.", 159 file.display() 160 ), 161 Self::InvalidMsrvType => writeln!( 162 stderr, 163 "'{PACKAGE}.{RUST_VERSION}' exists but is not a string nor table in {}.", 164 file.display() 165 ), 166 Self::Msrv => writeln!( 167 stderr, 168 "'{PACKAGE}.{RUST_VERSION}' is a string but is not a valid MSRV in {}.", 169 file.display() 170 ), 171 Self::MsrvWorkspaceMissing => writeln!( 172 stderr, 173 "'{PACKAGE}.{RUST_VERSION}' is a table but does not contain the key '{WORKSPACE}' in {}.", 174 file.display() 175 ), 176 Self::MsrvWorkspaceVal => writeln!( 177 stderr, 178 "'{PACKAGE}.{RUST_VERSION}.{WORKSPACE}' exists but is not a Boolean or is not true in {}.", 179 file.display() 180 ), 181 Self::WorkspaceIo(e) => writeln!( 182 stderr, 183 "There was an error looking for the workspace Cargo.toml in {} and its ancestor directories: {e}.", 184 file.parent().unwrap_or_else(|| unreachable!("there is a bug in main. manifest::Manifest::from_toml must be passed the absolute path to the package's Cargo.toml.")).display(), 185 ), 186 Self::WorkspaceLock(e) => writeln!( 187 stderr, 188 "There was an error locking the workspace Cargo.toml in {} and its ancestor directories: {e}.", 189 file.parent().unwrap_or_else(|| unreachable!("there is a bug in main. manifest::Manifest::from_toml must be passed the absolute path to the package's Cargo.toml.")).display(), 190 ), 191 Self::WorkspaceLenMismatch=> writeln!( 192 stderr, 193 "The length of the workspace Cargo.toml in {} does not match the length reported by the file systemn.", 194 file.parent().unwrap_or_else(|| unreachable!("there is a bug in main. manifest::Manifest::from_toml must be passed the absolute path to the package's Cargo.toml.")).display(), 195 ), 196 Self::WorkspaceDoesNotExist => writeln!( 197 stderr, 198 "There is no workspace Cargo.toml in {} nor its ancestor directories.", 199 file.parent().unwrap_or_else(|| unreachable!("there is a bug in main. manifest::Manifest::from_toml must be passed the absolute path to the package's Cargo.toml.")).display(), 200 ), 201 Self::InvalidWorkspaceType => writeln!( 202 stderr, 203 "'{PACKAGE}.{WORKSPACE}' exists but is not a string in {}.", 204 file.display() 205 ), 206 Self::WorkspaceRead(e, p) => { 207 writeln!(stderr, "There was an issue reading the workspace file {}: {e}.", p.display()) 208 } 209 Self::WorkspaceReadLock(e, p) => { 210 writeln!(stderr, "There was an issue locking the workspace file {}: {e}.", p.display()) 211 } 212 Self::WorkspaceReadLenMismatch(p) => { 213 writeln!(stderr, "The length of the workspace file {} does not match the length reported by the file system.", p.display()) 214 } 215 Self::WorkspaceToml(e, p) => write!( 216 stderr, 217 "Error parsing workspace file {} as TOML: {e}.", 218 p.display() 219 ), 220 Self::Workspace(e, p) => e.write(stderr, &p), 221 } 222 } 223 } 224 /// `"features"`. 225 const FEATURES: &str = "features"; 226 /// Error returned from extracting feature dependencies. 227 #[cfg_attr(test, derive(Debug, PartialEq))] 228 pub(crate) enum FeatureDependenciesErr { 229 /// Variant returned when a feature is not an array. 230 /// 231 /// The contained `String` is the name of the feature. 232 InvalidFeatureType(String), 233 /// Variant returned when a feature dependency is not a string. 234 /// 235 /// The contained `String` is the name of the feature. 236 InvalidDependencyType(String), 237 /// Variant returned when a feature dependency is not a feature nor dependency. 238 /// 239 /// The first contained `String` is the name of the feature, and the second `String` is the name of the invalid 240 /// feature dependency. 241 /// 242 /// Note this is only possible when `allow_implied_features` is `false` when passed to 243 /// [`Features::validate_dependencies`]. 244 InvalidDependency(String, String), 245 /// Variant returned when a feature is cyclic. 246 /// 247 /// The contained `String` is the name of the feature. 248 CyclicFeature(String), 249 /// Variant returned when a feature dependency is redundant. 250 /// 251 /// The first contained `String` is the name of the feature, and the second `String` is the name of the 252 /// redundant feature dependency. 253 RedundantDependency(String, String), 254 } 255 impl FeatureDependenciesErr { 256 /// Writes `self` to `stderr`. 257 fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> { 258 match self { 259 Self::InvalidFeatureType(name) => { 260 writeln!( 261 stderr, 262 "'{FEATURES}.{name}' is not an array in {}.", 263 file.display() 264 ) 265 } 266 Self::InvalidDependencyType(name) => writeln!( 267 stderr, 268 "'{FEATURES}.{name}' contains a value that is not a string in {}.", 269 file.display() 270 ), 271 Self::InvalidDependency(name, dep_name) => writeln!( 272 stderr, 273 "'{FEATURES}.{name}' contains '{dep_name}' which is neither a feature nor dependency in {}. It may be an implied feature from an optional dependency, but --allow-implied-features was not passed.", 274 file.display() 275 ), 276 Self::CyclicFeature(name) => writeln!( 277 stderr, 278 "'{FEATURES}.{name}' is a cyclic feature in {}.", 279 file.display() 280 ), 281 Self::RedundantDependency(name, dep_name) => writeln!( 282 stderr, 283 "'{FEATURES}.{name}' contains the redundant dependency '{dep_name}' in {}.", 284 file.display() 285 ), 286 } 287 } 288 } 289 /// Error returned from extracting `"features"`. 290 #[cfg_attr(test, derive(Debug, PartialEq))] 291 pub(crate) enum FeaturesErr { 292 /// Variant returned when `features` is not a table in Cargo.toml. 293 InvalidType, 294 /// Variant returned when `features` contains a feature with an invalid name. 295 /// 296 /// The contained `String` is the name of the feature. 297 InvalidName(String), 298 /// Variant returned when there is an issue with a feature's dependencies. 299 FeatureDependencies(FeatureDependenciesErr), 300 } 301 impl FeaturesErr { 302 /// Writes `self` to `stderr`. 303 fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> { 304 match self { 305 Self::InvalidType => { 306 writeln!( 307 stderr, 308 "'{FEATURES}' exists but is not a table in {}.", 309 file.display() 310 ) 311 } 312 Self::InvalidName(name) => { 313 writeln!( 314 stderr, 315 "'{FEATURES}.{name}' is not a valid feature name in {}.", 316 file.display() 317 ) 318 } 319 Self::FeatureDependencies(e) => e.write(stderr, file), 320 } 321 } 322 } 323 /// Error returned from extracting dependencies. 324 #[cfg_attr(test, derive(Debug, PartialEq))] 325 pub(crate) enum DependenciesErr { 326 /// Variant returned when the dependencies is not a table. 327 /// 328 /// The contained `str` is the name of the dependencies (e.g., `"dependencies"`). 329 Type(&'static str), 330 /// Variant returned when a dependency has an invalid name. 331 /// 332 /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the 333 /// name of the dependency. 334 Name(&'static str, String), 335 /// Variant returned when a dependency is not a string or table. 336 /// 337 /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the 338 /// name of the dependency. 339 DependencyType(&'static str, String), 340 /// Variant returned when a dependency contains an `"optional"` key whose value is not a Boolean. 341 /// 342 /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the 343 /// name of the dependency. 344 OptionalType(&'static str, String), 345 /// Variant returned when an optional dependency would cause an implied feature to be created. 346 /// 347 /// Note this is only possible when `allow_implied_features` is `false` when passed to 348 /// [`Features::add_optional_dependencies`]. 349 /// 350 /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the 351 /// name of the dependency. 352 ImpliedFeature(&'static str, String), 353 } 354 /// Error returned from extracting dependencies to add implied features. 355 #[cfg_attr(test, derive(Debug, PartialEq))] 356 pub(crate) enum ImpliedFeaturesErr { 357 /// Variant returned from extracting dependencies. 358 Dependencies(DependenciesErr), 359 /// Variant returned when `target` is not a table in Cargo.toml. 360 TargetType, 361 /// Variant returned when `target` contains a key whose value is not a table. 362 /// 363 /// The contained `String` is the name of the key. 364 TargetPlatformType(String), 365 /// Variant returned when a target platform contains an issue with dependencies. 366 /// 367 /// The contained `String` is the name of the target platform. 368 TagetPlatformDependencies(String, DependenciesErr), 369 /// Variant returned when a feature dependency is not a feature nor dependency. 370 /// 371 /// The first contained `String` is the name of the feature, and the second `String` is the name of the invalid 372 /// feature dependency. 373 /// 374 /// Note this is only possible when `allow_implied_features` is `true` when passed to 375 /// [`Features::validate_dependencies`] since when `false` we verify the the dependency 376 /// is defined as a feature. 377 InvalidDependency(String, String), 378 } 379 /// `"optional"`. 380 const OPTIONAL: &str = "optional"; 381 /// `"target"`. 382 const TARGET: &str = "target"; 383 impl ImpliedFeaturesErr { 384 /// Writes `self` to `stderr`. 385 fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> { 386 match self { 387 Self::Dependencies(e) => match e { 388 DependenciesErr::Type(name) => { 389 writeln!( 390 stderr, 391 "'{name}' exists but is not a table in {}.", 392 file.display() 393 ) 394 } 395 DependenciesErr::Name(name, dep_name) => { 396 writeln!( 397 stderr, 398 "'{name}.{dep_name}' is not a valid dependency name in {}.", 399 file.display() 400 ) 401 } 402 DependenciesErr::DependencyType(name, dep_name) => { 403 writeln!( 404 stderr, 405 "'{name}.{dep_name}' exists but is not a string nor table in {}.", 406 file.display() 407 ) 408 } 409 DependenciesErr::OptionalType(name, dep_name) => { 410 writeln!( 411 stderr, 412 "'{name}.{dep_name}.{OPTIONAL}' exists but is not a Boolean in {}.", 413 file.display() 414 ) 415 } 416 DependenciesErr::ImpliedFeature(name, dep_name) => { 417 writeln!( 418 stderr, 419 "'{name}.{dep_name}' causes an implied feature to be defined in {}, but implied features were forbidden. --allow-implied-features can be passed to allow it.", 420 file.display() 421 ) 422 } 423 }, 424 Self::TargetType => { 425 writeln!( 426 stderr, 427 "'{TARGET}' exists but is not a table in {}.", 428 file.display() 429 ) 430 } 431 Self::TargetPlatformType(name) => { 432 writeln!( 433 stderr, 434 "'{TARGET}.{name}' exists but is not a table in {}.", 435 file.display() 436 ) 437 } 438 Self::TagetPlatformDependencies(name, e) => match e { 439 DependenciesErr::Type(table_name) => { 440 writeln!( 441 stderr, 442 "'{TARGET}.{name}.{table_name}' exists but is not a table in {}.", 443 file.display() 444 ) 445 } 446 DependenciesErr::Name(table_name, dep_name) => { 447 writeln!( 448 stderr, 449 "'{TARGET}.{name}.{table_name}.{dep_name}' is not a valid dependency name in {}.", 450 file.display() 451 ) 452 } 453 DependenciesErr::DependencyType(table_name, dep_name) => writeln!( 454 stderr, 455 "'{TARGET}.{name}.{table_name}.{dep_name}' exists but is not a string nor table in {}.", 456 file.display() 457 ), 458 DependenciesErr::OptionalType(table_name, dep_name) => writeln!( 459 stderr, 460 "'{TARGET}.{name}.{table_name}.{dep_name}.{OPTIONAL}' exists but is not a Boolean in {}.", 461 file.display() 462 ), 463 DependenciesErr::ImpliedFeature(table_name, dep_name) => { 464 writeln!( 465 stderr, 466 "'{TARGET}.{name}.{table_name}.{dep_name}' causes an implied feature to be defined in {}, but implied features were forbidden. --allow-implied-features can be passed to allow it.", 467 file.display() 468 ) 469 } 470 }, 471 Self::InvalidDependency(name, dep_name) => writeln!( 472 stderr, 473 "'{FEATURES}.{name}' contains '{dep_name}' which is neither a feature nor dependency in {}.", 474 file.display() 475 ), 476 } 477 } 478 } 479 /// Error returned from parsing Cargo.toml. 480 #[cfg_attr(test, derive(Debug, PartialEq))] 481 pub(crate) enum ManifestErr { 482 /// Variant returned when Cargo.toml is not valid TOML. 483 Toml(TomlErr, PathBuf), 484 /// Variant returned when extracting `package`. 485 Package(PackageErr, PathBuf), 486 /// Variant returned when extracting `features`. 487 Features(FeaturesErr, PathBuf), 488 /// Variant returned when extracting dependencies in order to add implied features. 489 ImpliedFeatures(ImpliedFeaturesErr, PathBuf), 490 /// Variant returned when ignoring a feature that does not exist. 491 UndefinedIgnoreFeature(String, PathBuf), 492 } 493 impl ManifestErr { 494 /// Writes `self` to `stderr`. 495 pub(crate) fn write(self, mut stderr: StderrLock<'_>) -> Result<(), Error> { 496 match self { 497 Self::Toml(e, file) => write!( 498 stderr, 499 "Error parsing package file {} as TOML: {e}.", 500 file.display() 501 ), 502 Self::Package(e, file) => e.write(stderr, &file), 503 Self::Features(e, file) => e.write(stderr, &file), 504 Self::ImpliedFeatures(e, file) => e.write(stderr, &file), 505 Self::UndefinedIgnoreFeature(feature, file) => writeln!( 506 stderr, 507 "The feature '{feature}' was requested to be ignored, but it is not a feature in the package file {}.", 508 file.display() 509 ), 510 } 511 } 512 } 513 /// Error when there are too many features to create the power set. 514 #[cfg_attr(test, derive(Debug, PartialEq))] 515 pub(crate) struct TooManyFeaturesErr; 516 /// Parses `val` as a `u64` in decimal notation without leading 0s. 517 /// 518 /// # Errors 519 /// 520 /// Errors iff `val` is not a valid `u64` in decimal notation without leading 0s. 521 pub(crate) fn parse_int(val: &str) -> Result<u64, ()> { 522 val.as_bytes().first().ok_or(()).and_then(|fst| { 523 if *fst == b'0' { 524 if val.len() == 1 { Ok(0) } else { Err(()) } 525 } else { 526 val.parse().map_err(|_e| ()) 527 } 528 }) 529 } 530 /// MSRV in Cargo.toml. 531 #[cfg_attr(test, derive(Debug, PartialEq))] 532 pub(crate) struct Msrv { 533 /// Major version. 534 major: u64, 535 /// Minor version. 536 minor: Option<u64>, 537 /// Patch version. 538 patch: Option<u64>, 539 } 540 impl Msrv { 541 /// Converts `msrv` into `Self` based on a valid MSRV string. 542 #[expect(unsafe_code, reason = "comments justify their correctness")] 543 fn extract_msrv(msrv: &str) -> Result<Self, ()> { 544 let mut iter = msrv.as_bytes().split(|b| *b == b'.'); 545 iter.next().ok_or(()).and_then(|fst| { 546 // SAFETY: 547 // The original input is a `str` and we split on `b'.'` which is a single-byte 548 // UTF-8 code unit; thus we don't have to worry about splitting a multi-byte 549 // UTF-8 code unit. 550 let major_utf8 = unsafe { str::from_utf8_unchecked(fst) }; 551 parse_int(major_utf8).and_then(|major| { 552 iter.next().map_or_else( 553 || { 554 Ok(Self { 555 major, 556 minor: None, 557 patch: None, 558 }) 559 }, 560 |snd| { 561 // SAFETY: 562 // The original input is a `str` and we split on `b'.'` which is 563 // a single-byte UTF-8 code unit; thus we don't have to worry 564 // about splitting a multi-byte UTF-8 code unit. 565 let minor_utf8 = unsafe { str::from_utf8_unchecked(snd) }; 566 parse_int(minor_utf8).and_then(|minor_val| { 567 iter.next().map_or_else( 568 || { 569 Ok(Self { 570 major, 571 minor: Some(minor_val), 572 patch: None, 573 }) 574 }, 575 |lst| { 576 // SAFETY: 577 // The original input is a `str` and we split on 578 // `b'.'` which is a single-byte UTF-8 code 579 // unit; thus we don't have to worry about 580 // splitting a multi-byte UTF-8 code unit. 581 let patch_utf8 = unsafe { str::from_utf8_unchecked(lst) }; 582 parse_int(patch_utf8).and_then(|patch_val| { 583 iter.next().map_or_else( 584 || { 585 Ok(Self { 586 major, 587 minor: Some(minor_val), 588 patch: Some(patch_val), 589 }) 590 }, 591 |_| Err(()), 592 ) 593 }) 594 }, 595 ) 596 }) 597 }, 598 ) 599 }) 600 }) 601 } 602 /// Reads `workspace` from `toml` extracting the MSRV. 603 fn extract_workspace( 604 toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 605 ) -> Result<Self, WorkspaceErr> { 606 toml.get(WORKSPACE) 607 .ok_or(WorkspaceErr::Missing) 608 .and_then(|work_span| { 609 if let DeValue::Table(ref workspace) = *work_span.get_ref() { 610 workspace 611 .get(PACKAGE) 612 .ok_or(WorkspaceErr::MissingPackage) 613 .and_then(|pack_span| { 614 if let DeValue::Table(ref package) = *pack_span.get_ref() { 615 package 616 .get(RUST_VERSION) 617 .ok_or(WorkspaceErr::MissingPackageMsrv) 618 .and_then(|msrv_span| { 619 if let DeValue::String(ref msrv) = *msrv_span.get_ref() { 620 Self::extract_msrv(msrv) 621 .map_err(|()| WorkspaceErr::Msrv) 622 } else { 623 Err(WorkspaceErr::InvalidPackageMsrvType) 624 } 625 }) 626 } else { 627 Err(WorkspaceErr::InvalidPackageType) 628 } 629 }) 630 } else { 631 Err(WorkspaceErr::InvalidType) 632 } 633 }) 634 } 635 /// Recursively looks for `Cargo.toml` in `cur_dir` and ancestor directories until one is found 636 /// that contains a key named [`WORKSPACE`]. Once found, it's MSRV will be parsed and returned. 637 /// 638 /// We make this recursive in case the (impossible?) path traversal becomes cyclic; in which 639 /// we want a stack overflow to occur. 640 /// 641 /// Note if any error occurs not related to a not found file error, then this will error. 642 #[expect( 643 clippy::verbose_file_reads, 644 reason = "false positive since we want to lock the file" 645 )] 646 fn get_workspace_toml(mut cur_dir: PathBuf) -> Result<Self, PackageErr> { 647 cur_dir.push(super::cargo_toml()); 648 match File::options() 649 .read(true) 650 .open(&cur_dir) 651 { 652 Ok(mut file) => { 653 file.try_lock_shared() 654 .map_err(PackageErr::WorkspaceLock) 655 .and_then(|()| { 656 file.metadata() 657 .map_err(PackageErr::WorkspaceIo) 658 .and_then(|meta| { 659 let meta_len = usize::try_from(meta.len()).unwrap_or(usize::MAX); 660 let mut data_utf8 = Vec::with_capacity(meta_len); 661 file.read_to_end(&mut data_utf8) 662 .map_err(PackageErr::WorkspaceIo) 663 .and_then(|len| { 664 drop(file); 665 if meta_len == len { 666 String::from_utf8(data_utf8) 667 .map_err(|e| PackageErr::WorkspaceIo(Error::other(e))) 668 .and_then(|data| { 669 Map::parse(&data) 670 .map_err(|e| PackageErr::WorkspaceToml(e, cur_dir.clone())) 671 .and_then(|toml| { 672 let t = toml.into_inner(); 673 if t.contains_key(WORKSPACE) { 674 Self::extract_workspace(&t) 675 .map_err(|e| PackageErr::Workspace(e, cur_dir)) 676 } else { 677 _ = cur_dir.pop(); 678 if cur_dir.pop() { 679 Self::get_workspace_toml(cur_dir) 680 } else { 681 Err(PackageErr::WorkspaceDoesNotExist) 682 } 683 } 684 }) 685 }) 686 } else { 687 Err(PackageErr::WorkspaceLenMismatch) 688 } 689 }) 690 }) 691 }) 692 } 693 Err(e) => { 694 if matches!(e.kind(), ErrorKind::NotFound) { 695 _ = cur_dir.pop(); 696 if cur_dir.pop() { 697 Self::get_workspace_toml(cur_dir) 698 } else { 699 Err(PackageErr::WorkspaceIo(e)) 700 } 701 } else { 702 Err(PackageErr::WorkspaceIo(e)) 703 } 704 } 705 } 706 } 707 /// Extracts `"rust-version"` from `"package"` or `toml` in the case it's defined in a workspace. 708 #[expect( 709 clippy::panic_in_result_fn, 710 reason = "want to crash when there is a bug" 711 )] 712 #[expect( 713 clippy::verbose_file_reads, 714 reason = "false positive since we want to lock the file" 715 )] 716 fn extract_from_toml( 717 toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 718 package: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 719 cargo_toml: &Path, 720 ) -> Result<Option<Self>, PackageErr> { 721 package.get(RUST_VERSION).map_or(Ok(None), |msrv_span| { 722 match *msrv_span.get_ref() { 723 DeValue::String(ref msrv) => Self::extract_msrv(msrv) 724 .map_err(|()| PackageErr::Msrv) 725 .map(Some), 726 DeValue::Table(ref msrv) => msrv 727 .get(WORKSPACE) 728 .ok_or(PackageErr::MsrvWorkspaceMissing) 729 .and_then(|work| { 730 if matches!(*work.get_ref(), DeValue::Boolean(b) if b) { 731 package.get(WORKSPACE).map_or_else( 732 || if toml.contains_key(WORKSPACE) { 733 Self::extract_workspace(toml).map_err(|e| PackageErr::Workspace(e, cargo_toml.to_path_buf())).map(Some) 734 } else { 735 let mut search_path = cargo_toml.to_path_buf(); 736 assert!(search_path.pop(), "there is a bug in main. manifest::Manifest::from_toml must be passed the absolute path to the package's Cargo.toml."); 737 if search_path.pop() { 738 Self::get_workspace_toml(search_path).map(Some) 739 } else { 740 Err(PackageErr::WorkspaceDoesNotExist) 741 } 742 }, 743 |path_span| { 744 if let DeValue::String(ref workspace_path) = *path_span.get_ref() { 745 let mut path = cargo_toml.to_path_buf(); 746 assert!(path.pop(), "there is a bug in main. manifest::Manifest::from_toml must be passed the absolute path to the package's Cargo.toml."); 747 path.push(workspace_path.as_ref()); 748 path.push(super::cargo_toml()); 749 File::options().read(true).open(&path).map_err(|e| PackageErr::WorkspaceRead(e, path.clone())).and_then(|mut workspace_file| { 750 workspace_file.try_lock_shared().map_err(|e| PackageErr::WorkspaceReadLock(e, path.clone())).and_then(|()| { 751 workspace_file.metadata().map_err(|e| PackageErr::WorkspaceRead(e, path.clone())).and_then(|meta| { 752 let meta_len = usize::try_from(meta.len()).unwrap_or(usize::MAX); 753 let mut workspace_utf8 = Vec::with_capacity(meta_len); 754 workspace_file.read_to_end(&mut workspace_utf8).map_err(|e| PackageErr::WorkspaceRead(e, path.clone())).and_then(|len| { 755 drop(workspace_file); 756 if meta_len == len { 757 String::from_utf8(workspace_utf8).map_err(|e| PackageErr::WorkspaceRead(Error::other(e), path.clone())).and_then(|workspace_data| { 758 Map::parse(&workspace_data).map_err(|e| PackageErr::WorkspaceToml(e, path.clone())).and_then(|workspace_toml| Self::extract_workspace(workspace_toml.get_ref()).map_err(|e| PackageErr::Workspace(e, path)).map(Some)) 759 }) 760 } else { 761 Err(PackageErr::WorkspaceReadLenMismatch(path)) 762 } 763 }) 764 }) 765 }) 766 }) 767 } else { 768 Err(PackageErr::InvalidWorkspaceType) 769 } 770 }, 771 ) 772 } else { 773 Err(PackageErr::MsrvWorkspaceVal) 774 } 775 }), 776 DeValue::Integer(_) 777 | DeValue::Float(_) 778 | DeValue::Boolean(_) 779 | DeValue::Datetime(_) 780 | DeValue::Array(_) => Err(PackageErr::InvalidMsrvType), 781 } 782 }) 783 } 784 /// Returns `Some` containing the MSRV with `'+'` prepended iff `stable` is semantically greater than `self` 785 /// or the default toolchan is semantically not equivalent to `self`; otherwise returns `None`. 786 /// 787 /// When `stable` is semantically less than the MSRV, an error is returned. 788 pub(crate) fn compare_to_other( 789 &self, 790 default: bool, 791 rustup_home: Option<&Path>, 792 cargo_path: &Path, 793 cargo_home: Option<&Path>, 794 ) -> Result<Option<String>, Box<ToolchainErr>> { 795 if default { 796 Toolchain::Default(false) 797 } else { 798 Toolchain::Stable 799 } 800 .get_version(rustup_home, cargo_path, cargo_home) 801 .and_then(|stable_dflt_version| { 802 match self.major.cmp(&stable_dflt_version.major) { 803 Ordering::Less => Ok(true), 804 Ordering::Equal => self.minor.map_or_else( 805 || Ok(false), 806 |min| match min.cmp(&stable_dflt_version.minor) { 807 Ordering::Less => Ok(true), 808 Ordering::Equal => self.patch.map_or_else( 809 || Ok(false), 810 |pat| match pat.cmp(&stable_dflt_version.patch) { 811 Ordering::Less => Ok(true), 812 Ordering::Equal => Ok(false), 813 Ordering::Greater => { 814 if default { 815 Ok(true) 816 } else { 817 Err(Box::new(ToolchainErr::MsrvTooHigh)) 818 } 819 } 820 }, 821 ), 822 Ordering::Greater => { 823 if default { 824 Ok(true) 825 } else { 826 Err(Box::new(ToolchainErr::MsrvTooHigh)) 827 } 828 } 829 }, 830 ), 831 Ordering::Greater => { 832 if default { 833 Ok(true) 834 } else { 835 Err(Box::new(ToolchainErr::MsrvTooHigh)) 836 } 837 } 838 } 839 .and_then(|get_msrv| { 840 if get_msrv { 841 Toolchain::Msrv(&self.minor.map_or_else( 842 || format!("+{}", self.major), 843 |min| { 844 self.patch.map_or_else( 845 || format!("+{}.{min}", self.major), 846 |pat| format!("+{}.{min}.{pat}", self.major), 847 ) 848 }, 849 )) 850 .get_version(rustup_home, cargo_path, cargo_home) 851 .and_then(|msrv_version| { 852 if msrv_version.major == self.major 853 && self.minor.is_none_or(|minor| { 854 msrv_version.minor == minor 855 && self.patch.is_none_or(|patch| msrv_version.patch == patch) 856 }) 857 { 858 Ok(Some(format!( 859 "+{}.{}.{}", 860 msrv_version.major, msrv_version.minor, msrv_version.patch 861 ))) 862 } else { 863 Err(Box::new(ToolchainErr::MsrvNotCompatibleWithInstalledMsrv( 864 msrv_version, 865 ))) 866 } 867 }) 868 } else { 869 Ok(None) 870 } 871 }) 872 }) 873 } 874 } 875 /// `package` info. 876 #[cfg_attr(test, derive(Debug, PartialEq))] 877 pub(crate) struct Package { 878 /// `rust-version`. 879 msrv: Option<Msrv>, 880 /// `name`. 881 name: String, 882 } 883 impl Package { 884 /// MSRV. 885 pub(crate) const fn msrv(&self) -> Option<&Msrv> { 886 self.msrv.as_ref() 887 } 888 /// Name. 889 pub(crate) const fn name(&self) -> &str { 890 self.name.as_str() 891 } 892 /// Extracts `"package"` from `toml`. 893 fn extract_from_toml( 894 toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 895 cargo_toml: &Path, 896 ) -> Result<Self, PackageErr> { 897 toml.get(PACKAGE) 898 .ok_or(PackageErr::Missing) 899 .and_then(|pack_span| { 900 if let DeValue::Table(ref package) = *pack_span.get_ref() { 901 package 902 .get(NAME) 903 .ok_or(PackageErr::MissingName) 904 .and_then(|name_span| { 905 if let DeValue::String(ref name) = *name_span.get_ref() { 906 Msrv::extract_from_toml(toml, package, cargo_toml).map(|msrv| { 907 Self { 908 msrv, 909 name: name.clone().into_owned(), 910 } 911 }) 912 } else { 913 Err(PackageErr::InvalidNameType) 914 } 915 }) 916 } else { 917 Err(PackageErr::InvalidType) 918 } 919 }) 920 } 921 } 922 /// Returns `true` iff `nodes` is pairwise disconnected. 923 #[expect( 924 clippy::arithmetic_side_effects, 925 clippy::indexing_slicing, 926 reason = "comment justifies correctness" 927 )] 928 fn pairwise_disconnected(nodes: &[&str], features: &[(String, Vec<String>)]) -> bool { 929 /// `panic`s with a static message about the existence of a bug in [`Manifest::deserialize`]. 930 #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] 931 fn impossible<T>() -> T { 932 unreachable!( 933 "there is a bug in manifest::Manifest::deserialize where feature dependencies are not only features." 934 ) 935 } 936 /// Returns `true` iff `feature_deps` directly contains `feature` or indirectly in the `Vec<String>` 937 /// associated with it. 938 fn contains( 939 feature_deps: &[String], 940 feature: &str, 941 features: &[(String, Vec<String>)], 942 ) -> bool { 943 feature_deps.iter().any(|feat| { 944 feat == feature 945 || contains( 946 &features 947 .iter() 948 .find(|val| val.0 == *feat) 949 .unwrap_or_else(impossible) 950 .1, 951 feature, 952 features, 953 ) 954 }) 955 } 956 !nodes.iter().enumerate().any(|(idx, feature)| { 957 let feature_info = &features 958 .iter() 959 .find(|val| val.0 == **feature) 960 .unwrap_or_else(impossible) 961 .1; 962 // `idx < nodes.len()`, so overflow is not possible and indexing is fine. 963 nodes[idx + 1..].iter().any(|feat| { 964 contains(feature_info, feat, features) 965 || contains( 966 features 967 .iter() 968 .find(|val| val.0 == **feat) 969 .unwrap_or_else(impossible) 970 .1 971 .as_slice(), 972 feature, 973 features, 974 ) 975 }) 976 }) 977 } 978 /// Power set of [`Features`] returned from [`Features::power_set`]. 979 /// 980 /// Note this is technically not the power set of features since semantically equivalent sets are ignored. 981 /// 982 /// The last set iterated will always be the empty set. If no features in [`Features`] depend on another 983 /// feature, then this will always return the entire set of features first; otherwise the entire set will 984 /// never be returned. The expected cardinality of a set iterated decreases; thus while it will almost always 985 /// be possible for a set _A_ to have larger cardinality than a set _B_ even when _A_ is iterated before _B_, 986 /// the expected value is smaller. 987 /// 988 /// The reason we attempt, but don't guarantee, that the first set iterated corresponds to a semantically 989 /// equivalent set as the original set is to take advantage of the parallel compiliation that occurs. Typically 990 /// the more features one enables, the more dependencies and functionality is added. By compiling code that 991 /// maximizes this first, we take better advantage of how code is compiled; in contrast if we compiled fewer 992 /// features first, subsequent compilations with more features will have to happen. We don't guarantee this 993 /// though since it slightly complicates code; instead we iterate based on the _expected_ cardinality in 994 /// descending order. 995 /// 996 /// We don't implement `Iterator` since we want to re-use the same `String` that represents the set we are 997 /// returning. 998 #[cfg_attr(test, derive(Debug, PartialEq))] 999 pub(crate) struct PowerSet<'a> { 1000 /// The set of features. 1001 feats: &'a [(String, Vec<String>)], 1002 /// `true` iff there are more sets to iterate. 1003 has_remaining: bool, 1004 /// `true` iff `feats` has redundant features; thus requiring us to check if a given set should be returned. 1005 check_overlap: bool, 1006 /// The current element of the power set that we are to return. 1007 /// 1008 /// This gets decremented as we iterate sets. 1009 idx: usize, 1010 /// Intermediate buffer we use to check if a set contains redundant features. 1011 buffer: Vec<&'a str>, 1012 /// The set we return. 1013 /// 1014 /// This is of the form `"<feat_1>,<feat_2>,...,<feat_n>"`. 1015 set: String, 1016 /// Number of sets skipped due to an equivalence with a smaller set. 1017 skipped_sets_counter: usize, 1018 /// `true` iff they empty set should be skipped. 1019 /// 1020 /// This doesn't contribute to [`Self::skipped_sets_counter`]. 1021 skip_empty_set: bool, 1022 } 1023 impl<'a> PowerSet<'a> { 1024 /// Max cardinality of a set we allow to take the power set of. 1025 // usize::MAX = 2^usize::BITS - 1 >= usize::BITS since usize::MAX >= 0; 1026 // thus `usize::BITS as usize` is free from truncation. 1027 #[expect(clippy::as_conversions, reason = "comment justifies correctness")] 1028 const MAX_SET_LEN: usize = usize::BITS as usize; 1029 /// Returns the cardinality less one of the power set of a set 1030 /// whose cardinality is `set_len`. 1031 /// 1032 /// `set_len` MUST not be greater than [`Self::MAX_SET_LEN`]. 1033 #[expect( 1034 clippy::arithmetic_side_effects, 1035 reason = "comment justifies correctness" 1036 )] 1037 const fn len_minus_one(set_len: usize) -> usize { 1038 assert!( 1039 set_len <= Self::MAX_SET_LEN, 1040 "manifest::PowerSet::len_minus_one must be passed a `usize` no larger than PowerSet::MAX_SET_LEN" 1041 ); 1042 if set_len == Self::MAX_SET_LEN { 1043 usize::MAX 1044 } else { 1045 // We verified that `set_len <= usize::BITS`; thus 1046 // this won't overflow nor underflow since 2^0 = 1. 1047 (1 << set_len) - 1 1048 } 1049 } 1050 /// Returns the cardinality of `self`. 1051 /// 1052 /// Note this is constant and is not affected by iteration of 1053 /// `self`. This isn't the remaining length. [`Self::skip_empty_set`] 1054 /// contributes towards this value. 1055 #[expect( 1056 clippy::arithmetic_side_effects, 1057 reason = "comment justifies correctness" 1058 )] 1059 pub(crate) fn len(&self) -> NonZeroUsizePlus1 { 1060 // We don't allow construction of `PowerSet` unless the set of features 1061 // is no more than [`Self::MAX_SET_LEN`]; thus this won't `panic`. 1062 // Underflow won't occur either since we don't allow construction when the 1063 // set of features is empty and when we must skip the empty set. 1064 // `NonZeroUsizePlus1` treats `0` as `usize::MAX + 1`, so we use wrapping addition. 1065 // This will only wrapp when `self.features.len() == Self::MAX_SET_LEN` and `!self.skip_empty_set`. 1066 NonZeroUsizePlus1::new( 1067 (Self::len_minus_one(self.feats.len()) - usize::from(self.skip_empty_set)) 1068 .wrapping_add(1), 1069 ) 1070 } 1071 /// Contructs `Self` based on `features`. 1072 /// 1073 /// When iterating the sets, the empty set will be skipped iff `skip_empty_set`; 1074 /// but this _won't_ contribute to `skipped_sets_counter`. 1075 /// 1076 /// Returns `None` iff `features` is empty and `skip_empty_set`. 1077 fn new( 1078 features: &'a Features, 1079 skip_empty_set: bool, 1080 ) -> Result<Option<Self>, TooManyFeaturesErr> { 1081 let len = features.0.len(); 1082 match len { 1083 0 if skip_empty_set => Ok(None), 1084 ..=Self::MAX_SET_LEN => { 1085 let mut buffer = Vec::with_capacity(len); 1086 features.0.iter().fold((), |(), key| { 1087 buffer.push(key.0.as_ref()); 1088 }); 1089 let check_overlap = !pairwise_disconnected(buffer.as_slice(), &features.0); 1090 Ok(Some(Self { 1091 feats: &features.0, 1092 has_remaining: true, 1093 check_overlap, 1094 // We verified `len <= Self::MAX_SET_LEN`, so this won't `panic`. 1095 idx: Self::len_minus_one(len), 1096 buffer, 1097 // This won't overflow since `usize::MAX = 2^usize::BITS - 1`, `usize::BITS >= 16`, the max 1098 // value of `len` is `usize::BITS`. 1099 // 16 * usize::BITS < 2^usize::BITS for `usize::BITS > 6`. 1100 set: String::with_capacity(len << 4), 1101 skipped_sets_counter: 0, 1102 skip_empty_set, 1103 })) 1104 } 1105 _ => Err(TooManyFeaturesErr), 1106 } 1107 } 1108 /// Resets `self` such that iteration returns to the beginning. 1109 pub(crate) const fn reset(&mut self) { 1110 // We don't allow construction of `PowerSet` when the number of 1111 // features exceeds [`Self::MAX_SET_LEN`], so this won't `panic`. 1112 self.idx = Self::len_minus_one(self.feats.len()); 1113 self.has_remaining = true; 1114 self.skipped_sets_counter = 0; 1115 } 1116 /// Writes the next element into `self.buffer` even if the set contains overlapping features. 1117 #[expect( 1118 clippy::arithmetic_side_effects, 1119 reason = "comment justifies correctness" 1120 )] 1121 fn inner_next_set(&mut self) { 1122 self.buffer.clear(); 1123 self.feats.iter().enumerate().fold((), |(), (i, feat)| { 1124 if self.idx & (1 << i) != 0 { 1125 self.buffer.push(feat.0.as_str()); 1126 } 1127 }); 1128 if self.idx == 0 { 1129 self.has_remaining = false; 1130 // The empty set is always the last set. 1131 } else if self.idx == 1 && self.skip_empty_set { 1132 self.idx = 0; 1133 self.has_remaining = false; 1134 } else { 1135 // This won't underflow since `idx > 0`. 1136 self.idx -= 1; 1137 } 1138 } 1139 /// Transforms the current element into its string form. 1140 fn current_set(&mut self) { 1141 self.set.clear(); 1142 self.buffer.iter().fold((), |(), s| { 1143 self.set.push_str(s); 1144 self.set.push(','); 1145 }); 1146 // We remove the trailing comma. In the event `self.set` is empty, this does nothing. 1147 _ = self.set.pop(); 1148 } 1149 /// Returns the next set. 1150 /// 1151 /// This returns `None` iff there are no more sets to return. It will continue to return `None` 1152 /// unless [`Self::reset`] is called. 1153 pub(crate) fn next_set(&mut self) -> Option<&str> { 1154 self.next_set_with_skip_count().map(|tup| tup.0) 1155 } 1156 /// Returns the next set along with the number of set skipped thus far. 1157 /// 1158 /// This returns `None` iff there are no more sets to return. It will continue to return `None` 1159 /// unless [`Self::reset`] is called. 1160 #[expect( 1161 clippy::arithmetic_side_effects, 1162 reason = "comment justifies correctness" 1163 )] 1164 pub(crate) fn next_set_with_skip_count(&mut self) -> Option<(&str, usize)> { 1165 if self.has_remaining { 1166 if self.check_overlap { 1167 while self.has_remaining { 1168 self.inner_next_set(); 1169 if pairwise_disconnected(self.buffer.as_slice(), self.feats) { 1170 self.current_set(); 1171 return Some((&self.set, self.skipped_sets_counter)); 1172 } 1173 // This maxes at `usize::MAX` since we ensure a power set is not created on a 1174 // set with more than `usize::BITS` elements. 1175 // We set to set every time [`Self::reset`] is called as well. 1176 self.skipped_sets_counter += 1; 1177 } 1178 None 1179 } else { 1180 self.inner_next_set(); 1181 self.current_set(); 1182 Some((&self.set, self.skipped_sets_counter)) 1183 } 1184 } else { 1185 None 1186 } 1187 } 1188 } 1189 /// Dependency table. 1190 enum DepTable { 1191 /// Library dependencies. 1192 Dependencies, 1193 /// Build dependencies. 1194 BuildDependencies, 1195 } 1196 impl DepTable { 1197 /// Returns the string representation of `self`. 1198 const fn into_str(self) -> &'static str { 1199 match self { 1200 Self::Dependencies => "dependencies", 1201 Self::BuildDependencies => "build-dependencies", 1202 } 1203 } 1204 } 1205 /// `"dep:"`. 1206 const DEP: &[u8; 4] = b"dep:"; 1207 /// Returns `true` iff `utf8` begins with [`DEP`]. 1208 fn is_feature_dependency_a_dependency(utf8: &[u8]) -> bool { 1209 utf8.starts_with(DEP) 1210 } 1211 /// Returns `true` iff `name` does not contain a `'/'` nor begins with [`DEP`]. 1212 fn is_feature_dependency_a_feature(name: &str) -> bool { 1213 let utf8 = name.as_bytes(); 1214 !(utf8.contains(&b'/') || is_feature_dependency_a_dependency(utf8)) 1215 } 1216 /// Features in Cargo.toml. 1217 /// 1218 /// Note this contains a `Vec` instead of a `HashMap` or `BTreeMap` since we enforce that very few entries exist 1219 /// due to the exponential nature of generating the power set; thus making a `Vec` more efficient and faster. 1220 /// This size is so small that we don't even sort and perform a binary search. 1221 /// 1222 /// The `String` in the tuple represents the name of the feature, and the `Vec` in the tuple represents the 1223 /// feature dependencies. The feature dependencies will not contain any dependency that contains a `'/'` 1224 /// but will contain all others. The name of each feature will not contain a `'/'` nor begin with [`DEP`]. 1225 /// Each dependency that is a feature (i.e., does not begin with [`DEP`]) is well-defined (i.e., is a feature 1226 /// itself) when `false` is passed to [`Self::extract_from_toml`]; when `true` is passed, then feature dependencies 1227 /// that are features are not verified to be defined in `self` since implied features still have to be added. 1228 /// Each feature does not contain any cycles nor redundant dependencies. 1229 /// 1230 /// One must still add implied features caused from any optional dependencies iff `true` is passed to 1231 /// [`Self::extract_from_toml`]; after which, one needs to remove all dependencies that are not features and verify 1232 /// all dependencies that are features are defined in `self` in order for [`PowerSet`] to work correctly. A feature 1233 /// with name `<dependency>` needs to be added with an empty `Vec` of dependencies iff no features exist that have a 1234 /// dependency whose name is `"dep:<dependency>"` _and_ there doesn't already exist a feature with that name. 1235 /// An error MUST NOT happen if there is such a feature since different kinds of dependencies can have the 1236 /// same name. While this will allow for the situation where a feature is defined with the same name as 1237 /// an implied feature, that won't matter once `cargo` is run since it will error anyway. 1238 #[cfg_attr(test, derive(Debug, PartialEq))] 1239 pub(crate) struct Features(Vec<(String, Vec<String>)>); 1240 impl Features { 1241 /// Returns `true` iff `self` contains a feature named `"default"`. 1242 pub(crate) fn contains_default(&self) -> bool { 1243 self.0.iter().any(|f| f.0 == "default") 1244 } 1245 /// `panic`s with a static message about the existence of a bug in `validate_dependencies`. 1246 /// 1247 /// This is used in lieu of `unreachable` in `validate_dependencies`, `check_redundant_features` 1248 /// and `extract_feature_dependencies`. 1249 #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] 1250 fn impossible<T>() -> T { 1251 unreachable!("there is a bug in manifest::Features::validate_dependencies.") 1252 } 1253 /// Verifies dependencies associated with `feature` is valid. 1254 /// 1255 /// `dependencies` are assumed to be associated with `feature` which the pair of is checked to be 1256 /// a key-value pair from `features` iff `allow_implied_features`. This verifies the following: 1257 /// 1258 /// * `dependencies` is an array that only contains strings. 1259 /// * Each string in `dependencies` that does not contain a `'/'` nor begins with [`DEP`] is a key 1260 /// in `features` iff `allow_implied_features`. 1261 /// * `feature` nor any dependency that is a feature in `dependencies` is cyclic. 1262 /// 1263 /// `cycle_detection` MUST contain only `feature` when this is called externally. 1264 /// 1265 /// This MUST only be called by itself or [`Self::extract_feature_dependencies`]. 1266 fn validate_dependencies<'a>( 1267 feature: &str, 1268 dependencies: &'a DeValue<'_>, 1269 features: &'a Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 1270 cycle_detection: &mut Vec<&'a str>, 1271 allow_implied_features: bool, 1272 ) -> Result<(), FeatureDependenciesErr> { 1273 if let DeValue::Array(ref info) = *dependencies { 1274 info.iter().try_fold((), |(), dep_span| { 1275 if let DeValue::String(ref dep_name) = *dep_span.get_ref() { 1276 if is_feature_dependency_a_feature(dep_name) { 1277 if cycle_detection.contains(&dep_name.as_ref()) { 1278 Err(FeatureDependenciesErr::CyclicFeature( 1279 dep_name.clone().into_owned(), 1280 )) 1281 } else if let Some(next_feature) = features.get(dep_name.as_ref()) { 1282 cycle_detection.push(dep_name); 1283 Self::validate_dependencies( 1284 dep_name, 1285 next_feature.get_ref(), 1286 features, 1287 cycle_detection, 1288 allow_implied_features, 1289 ) 1290 .map(|()| { 1291 // We require calling code to add `feature` before calling this function. We 1292 // always add the most recent feature dependency. Therefore this is not empty. 1293 _ = cycle_detection.pop().unwrap_or_else(Self::impossible); 1294 }) 1295 } else if allow_implied_features { 1296 // `dep_name` may be an implied feature which we have yet to add. 1297 Ok(()) 1298 } else { 1299 // We require calling code to add `feature` before calling this function. We always 1300 // add the most recent feature dependency. Therefore this is not empty. 1301 Err(FeatureDependenciesErr::InvalidDependency( 1302 cycle_detection 1303 .pop() 1304 .unwrap_or_else(Self::impossible) 1305 .to_owned(), 1306 dep_name.clone().into_owned(), 1307 )) 1308 } 1309 } else { 1310 Ok(()) 1311 } 1312 } else { 1313 Err(FeatureDependenciesErr::InvalidDependencyType( 1314 feature.to_owned(), 1315 )) 1316 } 1317 }) 1318 } else { 1319 Err(FeatureDependenciesErr::InvalidFeatureType( 1320 feature.to_owned(), 1321 )) 1322 } 1323 } 1324 /// Verifies there are no redundant dependencies that are features in `dependencies`. 1325 /// 1326 /// Returns `true` iff there is a redundant dependency. 1327 /// 1328 /// This must be called _after_ `validate_dependencies` is called on the same arguments. 1329 fn check_redundant_dependencies( 1330 feature: &str, 1331 dependencies: &DeArray<'_>, 1332 features: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 1333 allow_implied_features: bool, 1334 ) -> bool { 1335 dependencies.iter().any(|dep_span| { 1336 if let DeValue::String(ref dep_name) = *dep_span.get_ref() { 1337 is_feature_dependency_a_feature(dep_name) 1338 && (feature == dep_name 1339 || features.get(dep_name.as_ref()).map_or_else( 1340 || { 1341 if allow_implied_features { 1342 false 1343 } else { 1344 // We require `validate_dependencies` to be called before this function which 1345 // ensures all features recursively in the `dependencies` are defined as features iff `!allow_implied_features`. 1346 Self::impossible() 1347 } 1348 }, 1349 |next_feature_span| { 1350 Self::check_redundant_dependencies( 1351 feature, 1352 next_feature_span 1353 .get_ref() 1354 .as_array() 1355 // We require `validate_dependencies` to be called before this function 1356 // which ensures all feature dependencies recursively are arrays. 1357 .unwrap_or_else(Self::impossible), 1358 features, 1359 allow_implied_features, 1360 ) 1361 }, 1362 )) 1363 } else { 1364 // We require `validate_dependencies` to be called before this function which ensures all 1365 // dependencies recursivley in `dependencies` are strings. 1366 Self::impossible() 1367 } 1368 }) 1369 } 1370 /// Extracts the feature dependencies associated with `feature`. 1371 /// 1372 /// `dependencies` are assumed to be associated with `feature` which the pair of is checked to be 1373 /// a key-value pair from `features` iff `allow_implied_features`. This verifies the following: 1374 /// 1375 /// * `dependencies` is an array that only contains strings. 1376 /// * Each string in `dependencies` that does not contain a `'/'` nor begins with [`DEP`] is a key 1377 /// in `features` iff `allow_implied_features`. 1378 /// * There is no redundant feature in `dependencies` where "redundant" means the following: 1379 /// * There are no cycles (e.g., feature = \["feature"] or feature = \["a"], a = \["b"], b = \["a"]). 1380 /// * No unnecessary dependencies that are features (e.g., feature = \["a", "a"] or feature = \["a", "b"] 1381 /// a = \["b"], b = \[]). 1382 /// * There are no duplicate dependencies in `dependencies` that beging with [`DEP`]. 1383 /// 1384 /// Note since all dependencies that contain a `'/'` are ignored, there may be duplicates of them. 1385 /// Also when checking for redundant features in `dependencies`, _only_ features are considered; thus 1386 /// something like the following is allowed: feature = \["dep:a", "a"], a = \["dep:a"]. 1387 /// 1388 /// This must only be called from [`Self::extract_from_toml`]. 1389 #[expect( 1390 clippy::arithmetic_side_effects, 1391 reason = "comment justifies correctness" 1392 )] 1393 fn extract_feature_dependencies<'a>( 1394 feature: &'a str, 1395 dependencies: &'a DeValue<'_>, 1396 features: &'a Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 1397 cycle_buffer: &mut Vec<&'a str>, 1398 allow_implied_features: bool, 1399 ) -> Result<Vec<String>, FeatureDependenciesErr> { 1400 // `Self::validate_dependencies` requires `cycle_buffer` to contain, and only contain, `feature`. 1401 cycle_buffer.clear(); 1402 cycle_buffer.push(feature); 1403 Self::validate_dependencies(feature, dependencies, features, cycle_buffer, allow_implied_features).and_then(|()| { 1404 // `validate_dependencies` ensures `dependencies` is an array. 1405 let deps = dependencies.as_array().unwrap_or_else(Self::impossible); 1406 let mut vec_deps = Vec::with_capacity(deps.len()); 1407 deps.iter().enumerate().try_fold((), |(), (idx, dep_span)| if let DeValue::String(ref dep_name) = *dep_span.get_ref() { 1408 let dep_utf8 = dep_name.as_bytes(); 1409 if dep_utf8.contains(&b'/') { 1410 Ok(()) 1411 } else if is_feature_dependency_a_dependency(dep_utf8) { 1412 if vec_deps.iter().any(|d| d == dep_name) { 1413 Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), dep_name.clone().into_owned())) 1414 } else { 1415 vec_deps.push(dep_name.clone().into_owned()); 1416 Ok(()) 1417 } 1418 } else if let Some(next_feature_span) = features.get(dep_name.as_ref()) { 1419 // `validate_dependencies` ensures all feature 1420 // dependencies recursively are arrays. 1421 let feat_info = next_feature_span.get_ref().as_array().unwrap_or_else(Self::impossible); 1422 // `idx < deps.iter().len()`; thus this won't overflow. 1423 deps.iter().skip(idx + 1).try_fold((), |(), next_dep_span| if let DeValue::String(ref next_dep_name) = *next_dep_span.get_ref() { 1424 if is_feature_dependency_a_feature(next_dep_name) { 1425 if dep_name == next_dep_name { 1426 Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), dep_name.clone().into_owned())) 1427 } else if Self::check_redundant_dependencies(next_dep_name, feat_info, features, allow_implied_features) { 1428 Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), next_dep_name.clone().into_owned())) 1429 } else { 1430 features.get(next_dep_name.as_ref()).map_or_else( 1431 || { 1432 if allow_implied_features { 1433 Ok(()) 1434 } else { 1435 // `validate_dependencies` ensures all features 1436 // recursively in the feature dependencies are defined 1437 // as features iff `!allow_implied_features`. 1438 Self::impossible() 1439 } 1440 }, 1441 |next_dep_feature_span| { 1442 // `validate_dependencies` ensures all feature 1443 // dependencies recursively are arrays. 1444 if Self::check_redundant_dependencies(dep_name, next_dep_feature_span.get_ref().as_array().unwrap_or_else(Self::impossible), features, allow_implied_features) { 1445 Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), dep_name.clone().into_owned())) 1446 } else { 1447 Ok(()) 1448 } 1449 } 1450 ) 1451 } 1452 } else { 1453 Ok(()) 1454 } 1455 } else { 1456 // `validate_dependencies` ensures all dependencies recursively in `dependencies` are 1457 // strings. 1458 Self::impossible() 1459 }).map(|()| vec_deps.push(dep_name.clone().into_owned())) 1460 } else if allow_implied_features { 1461 vec_deps.push(dep_name.clone().into_owned()); 1462 Ok(()) 1463 } else { 1464 // `validate_dependencies` ensures all features 1465 // recursively in `dependencies` are defined as features 1466 // iff `!allow_implied_features`. 1467 Self::impossible() 1468 } 1469 } else { 1470 // `validate_dependencies` ensures all dependencies recursively in `dependencies` are strings. 1471 Self::impossible() 1472 }).map(|()| vec_deps) 1473 }) 1474 } 1475 /// Extracts `"features"` from `toml`. 1476 fn extract_from_toml( 1477 toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 1478 allow_implied_features: bool, 1479 ) -> Result<Self, FeaturesErr> { 1480 toml.get(FEATURES).map_or_else( 1481 || Ok(Self(Vec::new())), 1482 |features_span| { 1483 if let DeValue::Table(ref features) = *features_span.get_ref() { 1484 let mut cycle_buffer = Vec::with_capacity(features.len()); 1485 let mut feats = Vec::with_capacity(features.len()); 1486 features 1487 .iter() 1488 .try_fold((), |(), (name_span, feature_span)| { 1489 let name = name_span.get_ref(); 1490 if is_feature_dependency_a_feature(name) { 1491 Self::extract_feature_dependencies( 1492 name, 1493 feature_span.get_ref(), 1494 features, 1495 &mut cycle_buffer, 1496 allow_implied_features, 1497 ) 1498 .map_err(FeaturesErr::FeatureDependencies) 1499 .map(|deps| { 1500 feats.push((name.clone().into_owned(), deps)); 1501 }) 1502 } else { 1503 Err(FeaturesErr::InvalidName(name.clone().into_owned())) 1504 } 1505 }) 1506 .map(|()| Self(feats)) 1507 } else { 1508 Err(FeaturesErr::InvalidType) 1509 } 1510 }, 1511 ) 1512 } 1513 /// Extracts optional dependencies and adds their corresponding implied feature to `self` iff 1514 /// `allow_implied_features` and it's appropriate to do so. 1515 /// 1516 /// This must only be called from [`Self::add_implied_features`]. 1517 fn add_optional_dependencies( 1518 &mut self, 1519 toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 1520 table: DepTable, 1521 allow_implied_features: bool, 1522 ) -> Result<(), DependenciesErr> { 1523 let table_name = table.into_str(); 1524 toml.get(table_name).map_or(Ok(()), |deps_span| { 1525 if let DeValue::Table(ref deps) = *deps_span.get_ref() { 1526 deps.iter().try_fold((), |(), dep_span| { 1527 let dep_name = dep_span.0.get_ref(); 1528 if is_feature_dependency_a_feature(dep_name) { 1529 match *dep_span.1.get_ref() { 1530 DeValue::String(_) => Ok(()), 1531 DeValue::Table(ref dep_info) => { 1532 dep_info.get(OPTIONAL).map_or(Ok(()), |opt_span| { 1533 if let DeValue::Boolean(ref optional) = *opt_span.get_ref() { 1534 if *optional { 1535 self.0 1536 .iter() 1537 .try_fold((), |(), feat| { 1538 if feat.0 == *dep_name { 1539 // We already have a feature with the same name, 1540 // so we don't need to continue. 1541 Err(()) 1542 } else if feat.1.iter().any(|feat_dep| { 1543 feat_dep 1544 .as_bytes() 1545 .split_at_checked(DEP.len()) 1546 .is_some_and(|(pref, rem)| { 1547 pref == DEP 1548 && dep_name.as_bytes() == rem 1549 }) 1550 }) { 1551 // The feature dependencies contain `"dep:<dep_name>"`, 1552 // so we don't need to add an implied feature. 1553 Err(()) 1554 } else { 1555 // The feature name is not `<dep_name>` and all of 1556 // feature dependencies of all features are not named 1557 // `"dep:<dep_name>"`; thus we need to continue our 1558 // search. 1559 Ok(()) 1560 } 1561 }) 1562 .map_or(Ok(()), |()| { 1563 if allow_implied_features { 1564 // There is no feature with the name `<dep_name>` nor 1565 // are there any features that contain a feature 1566 // dependency named `"dep:<dep_name>"`; thus we must 1567 // insert an implied feature. 1568 self.0.push(( 1569 dep_name.clone().into_owned(), 1570 Vec::new(), 1571 )); 1572 Ok(()) 1573 } else { 1574 Err(DependenciesErr::ImpliedFeature( 1575 table_name, 1576 dep_name.clone().into_owned(), 1577 )) 1578 } 1579 }) 1580 } else { 1581 Ok(()) 1582 } 1583 } else { 1584 Err(DependenciesErr::OptionalType( 1585 table_name, 1586 dep_name.clone().into_owned(), 1587 )) 1588 } 1589 }) 1590 } 1591 DeValue::Integer(_) 1592 | DeValue::Float(_) 1593 | DeValue::Boolean(_) 1594 | DeValue::Datetime(_) 1595 | DeValue::Array(_) => Err(DependenciesErr::DependencyType( 1596 table_name, 1597 dep_name.clone().into_owned(), 1598 )), 1599 } 1600 } else { 1601 Err(DependenciesErr::Name( 1602 table_name, 1603 dep_name.clone().into_owned(), 1604 )) 1605 } 1606 }) 1607 } else { 1608 Err(DependenciesErr::Type(table_name)) 1609 } 1610 }) 1611 } 1612 /// Adds implied features to `self` based on the optional dependencies in `toml` iff `allow_implied_features`. 1613 fn add_implied_features( 1614 &mut self, 1615 toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>, 1616 allow_implied_features: bool, 1617 ) -> Result<(), ImpliedFeaturesErr> { 1618 self.add_optional_dependencies(toml, DepTable::Dependencies, allow_implied_features).map_err(ImpliedFeaturesErr::Dependencies).and_then(|()| self.add_optional_dependencies(toml, DepTable::BuildDependencies, allow_implied_features).map_err(ImpliedFeaturesErr::Dependencies).and_then(|()| toml.get(TARGET).map_or_else( 1619 || Ok(()), 1620 |target_span| { 1621 if let DeValue::Table(ref target) = *target_span.get_ref() { 1622 target.iter().try_fold((), |(), target_platform_span| { 1623 if let DeValue::Table(ref target_platform) = *target_platform_span.1.get_ref() { 1624 self.add_optional_dependencies(target_platform, DepTable::Dependencies, allow_implied_features).map_err(|e| ImpliedFeaturesErr::TagetPlatformDependencies(target_platform_span.0.get_ref().clone().into_owned(), e)).and_then(|()| self.add_optional_dependencies(target_platform, DepTable::BuildDependencies, allow_implied_features).map_err(|e| ImpliedFeaturesErr::TagetPlatformDependencies(target_platform_span.0.get_ref().clone().into_owned(), e))) 1625 } else { 1626 Err(ImpliedFeaturesErr::TargetPlatformType(target_platform_span.0.get_ref().clone().into_owned())) 1627 } 1628 }) 1629 } else { 1630 Err(ImpliedFeaturesErr::TargetType) 1631 } 1632 } 1633 ).and_then(|()| { 1634 if allow_implied_features { 1635 // We don't have to worry about cyclic features or anything other than the lack of a feature with 1636 // the name of the feature dependency. 1637 self.0.iter().try_fold((), |(), feature| feature.1.iter().try_fold((), |(), dep| { 1638 // We didn't save any feature dependencies that contain `'/'`, so we simply have to check if 1639 // a dependency begins with [`DEP`] to skip it. 1640 if is_feature_dependency_a_dependency(dep.as_bytes()) || self.0.iter().any(|other_feature| other_feature.0 == *dep) { 1641 Ok(()) 1642 } else { 1643 Err(ImpliedFeaturesErr::InvalidDependency(feature.0.clone(), dep.clone())) 1644 } 1645 })) 1646 } else { 1647 // When `!allowed_implied_features`, [`Self::validate_dependencies`] verifies non-dependency 1648 // feature dependencies are defined as features. 1649 Ok(()) 1650 } 1651 }))) 1652 } 1653 /// Returns the power set of `self` with semantically equivalent sets removed. 1654 /// 1655 /// The empty set of features is skipped iff `skip_no_feats`. None is only 1656 /// returned if there are no sets of features. This is only possible iff 1657 /// `self` contains no features and `skip_no_feats`. 1658 pub(crate) fn power_set( 1659 &self, 1660 skip_no_feats: bool, 1661 ) -> Result<Option<PowerSet<'_>>, TooManyFeaturesErr> { 1662 PowerSet::new(self, skip_no_feats) 1663 } 1664 } 1665 /// Package and features in `Cargo.toml`. 1666 #[cfg_attr(test, derive(Debug, PartialEq))] 1667 pub(crate) struct Manifest { 1668 /// The package. 1669 package: Package, 1670 /// The features. 1671 features: Features, 1672 } 1673 impl Manifest { 1674 /// Returns the defined MSRV iff there was one defined. 1675 pub(crate) const fn package(&self) -> &Package { 1676 &self.package 1677 } 1678 /// Returns the defined features. 1679 /// 1680 /// Note the returned `Features` doesn't have any cyclic features, each feature dependency for a given 1681 /// feature is a feature itself, and there are no redundant feature dependencies for a given feature. 1682 pub(crate) const fn features(&self) -> &Features { 1683 &self.features 1684 } 1685 /// Returns the data needed from `Cargo.toml`. 1686 /// 1687 /// Note `ignore_features` MUST not contain an empty `String`. 1688 #[expect( 1689 clippy::arithmetic_side_effects, 1690 reason = "comments justify correctness" 1691 )] 1692 #[expect( 1693 clippy::needless_pass_by_value, 1694 reason = "want to drop `val` as soon as possible" 1695 )] 1696 pub(crate) fn from_toml( 1697 val: String, 1698 allow_implied_features: bool, 1699 cargo_toml: &Path, 1700 ignore_features: &[String], 1701 ) -> Result<Self, Box<ManifestErr>> { 1702 Map::parse(val.as_str()) 1703 .map_err(|e| Box::new(ManifestErr::Toml(e, cargo_toml.to_path_buf()))) 1704 .and_then(|span| { 1705 let cargo = span.get_ref(); 1706 Package::extract_from_toml(cargo, cargo_toml) 1707 .map_err(|e| Box::new(ManifestErr::Package(e, cargo_toml.to_path_buf()))) 1708 .and_then(|package| { 1709 Features::extract_from_toml(cargo, allow_implied_features) 1710 .map_err(|e| { 1711 Box::new(ManifestErr::Features(e, cargo_toml.to_path_buf())) 1712 }) 1713 .and_then(|mut features| { 1714 features 1715 .add_implied_features(cargo, allow_implied_features) 1716 .map_err(|e| { 1717 Box::new(ManifestErr::ImpliedFeatures( 1718 e, 1719 cargo_toml.to_path_buf(), 1720 )) 1721 }) 1722 .and_then(|()| { 1723 features.0.iter_mut().fold( 1724 (), 1725 |(), &mut (_, ref mut deps)| { 1726 deps.retain(|d| { 1727 // We retain only features. Since we didn't save any 1728 // dependencies that contain `'/'`, it's slightly faster to just 1729 // check that a feature dependency is not a dependency. 1730 !is_feature_dependency_a_dependency( 1731 d.as_bytes(), 1732 ) 1733 }); 1734 }, 1735 ); 1736 // First we ensure all features we are to ignore are defined; 1737 // while doing this, we remove the feature. Note `ignore_features` 1738 // and `features` only contain distinct features, so we simply 1739 // have to check for the first occurrence. 1740 // Note calling code is required to have removed the empty 1741 // string if it had existed. 1742 ignore_features 1743 .iter() 1744 .try_fold((), |(), ig_feat| { 1745 features 1746 .0 1747 .iter() 1748 .try_fold(0, |idx, info| { 1749 if info.0 == *ig_feat { 1750 Err(idx) 1751 } else { 1752 // Clearly free from overflow. 1753 Ok(idx + 1) 1754 } 1755 }) 1756 .map_or_else( 1757 |idx| { 1758 drop(features.0.swap_remove(idx)); 1759 Ok(()) 1760 }, 1761 |_| { 1762 Err(Box::new( 1763 ManifestErr::UndefinedIgnoreFeature( 1764 ig_feat.clone(), 1765 cargo_toml.to_path_buf(), 1766 ), 1767 )) 1768 }, 1769 ) 1770 }) 1771 .map(|()| { 1772 // Now we remove all features that depend on the 1773 // features we are to ignore. 1774 ignore_features.iter().fold((), |(), ig_feat| { 1775 features.0.retain(|info| { 1776 !info.1.iter().any(|d| d == ig_feat) 1777 }); 1778 }); 1779 Self { package, features } 1780 }) 1781 }) 1782 }) 1783 }) 1784 }) 1785 } 1786 } 1787 #[cfg(test)] 1788 mod tests { 1789 use super::{ 1790 DependenciesErr, FeatureDependenciesErr, Features, FeaturesErr, ImpliedFeaturesErr, 1791 Manifest, ManifestErr, Msrv, NonZeroUsizePlus1, Package, PackageErr, Path, PathBuf, 1792 PowerSet, TooManyFeaturesErr, TryLockError, WorkspaceErr, 1793 }; 1794 impl PartialEq for PackageErr { 1795 #[expect(clippy::cognitive_complexity, reason = "long match expression")] 1796 fn eq(&self, other: &Self) -> bool { 1797 match *self { 1798 Self::Missing => matches!(*other, Self::Missing), 1799 Self::InvalidType => matches!(*other, Self::InvalidType), 1800 Self::MissingName => matches!(*other, Self::MissingName), 1801 Self::InvalidNameType => matches!(*other, Self::InvalidNameType), 1802 Self::InvalidMsrvType => matches!(*other, Self::InvalidMsrvType), 1803 Self::Msrv => matches!(*other, Self::Msrv), 1804 Self::MsrvWorkspaceMissing => matches!(*other, Self::MsrvWorkspaceMissing), 1805 Self::MsrvWorkspaceVal => matches!(*other, Self::MsrvWorkspaceVal), 1806 Self::InvalidWorkspaceType => matches!(*other, Self::InvalidWorkspaceType), 1807 Self::WorkspaceIo(ref e) => { 1808 matches!(*other, Self::WorkspaceIo(ref e2) if e.kind() == e2.kind()) 1809 } 1810 Self::WorkspaceLock(ref e) => { 1811 matches!(*other, Self::WorkspaceLock(ref e2) if match *e { 1812 TryLockError::Error(ref inner_e) => matches!(*e2, TryLockError::Error(ref inner_e2) if inner_e.kind() == inner_e2.kind()), 1813 TryLockError::WouldBlock => matches!(*e2, TryLockError::WouldBlock), 1814 }) 1815 } 1816 Self::WorkspaceLenMismatch => matches!(*other, Self::WorkspaceLenMismatch), 1817 Self::WorkspaceDoesNotExist => matches!(*other, Self::WorkspaceDoesNotExist), 1818 Self::WorkspaceRead(ref e, ref p) => { 1819 matches!(*other, Self::WorkspaceRead(ref e2, ref p2) if e.kind() == e2.kind() && p == p2) 1820 } 1821 Self::WorkspaceReadLock(ref e, ref p) => { 1822 matches!(*other, Self::WorkspaceReadLock(ref e2, ref p2) if p == p2 && match *e { 1823 TryLockError::Error(ref inner_e) => matches!(*e2, TryLockError::Error(ref inner_e2) if inner_e.kind() == inner_e2.kind()), 1824 TryLockError::WouldBlock => matches!(*e2, TryLockError::WouldBlock), 1825 }) 1826 } 1827 Self::WorkspaceReadLenMismatch(ref p) => { 1828 matches!(*other, Self::WorkspaceReadLenMismatch(ref p2) if p == p2) 1829 } 1830 Self::WorkspaceToml(ref e, ref p) => { 1831 matches!(*other, Self::WorkspaceToml(ref e2, ref p2) if e == e2 && p == p2) 1832 } 1833 Self::Workspace(e, ref p) => { 1834 matches!(*other, Self::Workspace(e2, ref p2) if e == e2 && p == p2) 1835 } 1836 } 1837 } 1838 } 1839 #[expect( 1840 clippy::cognitive_complexity, 1841 clippy::too_many_lines, 1842 reason = "want to test a lot of things" 1843 )] 1844 #[test] 1845 fn cargo_toml() { 1846 assert!( 1847 Manifest::from_toml("a".to_owned(), false, Path::new(""), &[]) 1848 .map_or_else(|e| matches!(*e, ManifestErr::Toml(_, _)), |_| false) 1849 ); 1850 assert_eq!( 1851 Manifest::from_toml(String::new(), false, Path::new(""), &[]), 1852 Err(Box::new(ManifestErr::Package( 1853 PackageErr::Missing, 1854 PathBuf::new() 1855 ))) 1856 ); 1857 assert_eq!( 1858 Manifest::from_toml("[' package']".to_owned(), false, Path::new(""), &[]), 1859 Err(Box::new(ManifestErr::Package( 1860 PackageErr::Missing, 1861 PathBuf::new() 1862 ))) 1863 ); 1864 assert_eq!( 1865 Manifest::from_toml("['package ']".to_owned(), false, Path::new(""), &[]), 1866 Err(Box::new(ManifestErr::Package( 1867 PackageErr::Missing, 1868 PathBuf::new() 1869 ))) 1870 ); 1871 assert_eq!( 1872 Manifest::from_toml("package=2".to_owned(), false, Path::new(""), &[]), 1873 Err(Box::new(ManifestErr::Package( 1874 PackageErr::InvalidType, 1875 PathBuf::new() 1876 ))) 1877 ); 1878 assert_eq!( 1879 Manifest::from_toml("[package]".to_owned(), false, Path::new(""), &[]), 1880 Err(Box::new(ManifestErr::Package( 1881 PackageErr::MissingName, 1882 PathBuf::new() 1883 ))) 1884 ); 1885 assert_eq!( 1886 Manifest::from_toml("[package]\nname=true".to_owned(), false, Path::new(""), &[]), 1887 Err(Box::new(ManifestErr::Package( 1888 PackageErr::InvalidNameType, 1889 PathBuf::new() 1890 ))) 1891 ); 1892 assert_eq!( 1893 Manifest::from_toml( 1894 "[package]\nname=\"\"\n\nrust-version=2".to_owned(), 1895 false, 1896 Path::new(""), 1897 &[] 1898 ), 1899 Err(Box::new(ManifestErr::Package( 1900 PackageErr::InvalidMsrvType, 1901 PathBuf::new() 1902 ))) 1903 ); 1904 assert_eq!( 1905 Manifest::from_toml( 1906 "[package]\nname=\"\"\nrust-version=\"\"".to_owned(), 1907 false, 1908 Path::new(""), 1909 &[] 1910 ), 1911 Err(Box::new(ManifestErr::Package( 1912 PackageErr::Msrv, 1913 PathBuf::new() 1914 ))) 1915 ); 1916 assert_eq!( 1917 Manifest::from_toml( 1918 "[package]\nname=\"\"\nrust-version=\"a\"".to_owned(), 1919 false, 1920 Path::new(""), 1921 &[] 1922 ), 1923 Err(Box::new(ManifestErr::Package( 1924 PackageErr::Msrv, 1925 PathBuf::new() 1926 ))) 1927 ); 1928 assert_eq!( 1929 Manifest::from_toml( 1930 "[package]\nname=\"\"\nrust-version=\"1.00.0\"".to_owned(), 1931 false, 1932 Path::new(""), 1933 &[] 1934 ), 1935 Err(Box::new(ManifestErr::Package( 1936 PackageErr::Msrv, 1937 PathBuf::new() 1938 ))) 1939 ); 1940 assert_eq!( 1941 Manifest::from_toml( 1942 "[package]\nname=\"\"\nrust-version=\"1..0\"".to_owned(), 1943 false, 1944 Path::new(""), 1945 &[] 1946 ), 1947 Err(Box::new(ManifestErr::Package( 1948 PackageErr::Msrv, 1949 PathBuf::new() 1950 ))) 1951 ); 1952 assert_eq!( 1953 Manifest::from_toml( 1954 "[package]\nname=\"\"\nrust-version=\"1.\"".to_owned(), 1955 false, 1956 Path::new(""), 1957 &[] 1958 ), 1959 Err(Box::new(ManifestErr::Package( 1960 PackageErr::Msrv, 1961 PathBuf::new() 1962 ))) 1963 ); 1964 assert_eq!( 1965 Manifest::from_toml( 1966 "[package]\nname=\"\"\nrust-version=\"01.0.0\"".to_owned(), 1967 false, 1968 Path::new(""), 1969 &[] 1970 ), 1971 Err(Box::new(ManifestErr::Package( 1972 PackageErr::Msrv, 1973 PathBuf::new() 1974 ))) 1975 ); 1976 assert_eq!( 1977 Manifest::from_toml( 1978 "[package]\nname=\"\"\nrust-version=\"1.0.0.1\"".to_owned(), 1979 false, 1980 Path::new(""), 1981 &[] 1982 ), 1983 Err(Box::new(ManifestErr::Package( 1984 PackageErr::Msrv, 1985 PathBuf::new() 1986 ))) 1987 ); 1988 assert_eq!( 1989 Manifest::from_toml( 1990 "[package]\nname=\"\"\nrust-version=\"111111111111111111111111.2.3\"".to_owned(), 1991 false, 1992 Path::new(""), 1993 &[] 1994 ), 1995 Err(Box::new(ManifestErr::Package( 1996 PackageErr::Msrv, 1997 PathBuf::new() 1998 ))) 1999 ); 2000 assert_eq!( 2001 Manifest::from_toml( 2002 "[package]\nname=\"\"\nrust-version=\"1.0.0-nightly\"".to_owned(), 2003 false, 2004 Path::new(""), 2005 &[] 2006 ), 2007 Err(Box::new(ManifestErr::Package( 2008 PackageErr::Msrv, 2009 PathBuf::new() 2010 ))) 2011 ); 2012 assert_eq!( 2013 Manifest::from_toml( 2014 "[package]\nname=\"\"\nrust-version=\"-1.0.0\"".to_owned(), 2015 false, 2016 Path::new(""), 2017 &[] 2018 ), 2019 Err(Box::new(ManifestErr::Package( 2020 PackageErr::Msrv, 2021 PathBuf::new() 2022 ))) 2023 ); 2024 assert_eq!( 2025 Manifest::from_toml( 2026 "[package]\nname=\"\"\nrust-version=\" 1.0.0\"".to_owned(), 2027 false, 2028 Path::new(""), 2029 &[] 2030 ), 2031 Err(Box::new(ManifestErr::Package( 2032 PackageErr::Msrv, 2033 PathBuf::new() 2034 ))) 2035 ); 2036 assert_eq!( 2037 Manifest::from_toml( 2038 "[package]\nname=\"\"\nrust-version=\"1.0.0 \"".to_owned(), 2039 false, 2040 Path::new(""), 2041 &[] 2042 ), 2043 Err(Box::new(ManifestErr::Package( 2044 PackageErr::Msrv, 2045 PathBuf::new() 2046 ))) 2047 ); 2048 assert_eq!( 2049 Manifest::from_toml( 2050 "[package]\nname=\"\"\nrust-version={}".to_owned(), 2051 false, 2052 Path::new(""), 2053 &[] 2054 ), 2055 Err(Box::new(ManifestErr::Package( 2056 PackageErr::MsrvWorkspaceMissing, 2057 PathBuf::new() 2058 ))) 2059 ); 2060 assert_eq!( 2061 Manifest::from_toml( 2062 "[package]\nname=\"\"\nrust-version={workspace=2}".to_owned(), 2063 false, 2064 Path::new(""), 2065 &[] 2066 ), 2067 Err(Box::new(ManifestErr::Package( 2068 PackageErr::MsrvWorkspaceVal, 2069 PathBuf::new() 2070 ))) 2071 ); 2072 assert_eq!( 2073 Manifest::from_toml( 2074 "[package]\nname=\"\"\nrust-version={workspace=false}".to_owned(), 2075 false, 2076 Path::new(""), 2077 &[] 2078 ), 2079 Err(Box::new(ManifestErr::Package( 2080 PackageErr::MsrvWorkspaceVal, 2081 PathBuf::new() 2082 ))) 2083 ); 2084 assert_eq!( 2085 Manifest::from_toml( 2086 "[package]\nname=\"\"\nrust-version={workspace=true}\nworkspace=2".to_owned(), 2087 false, 2088 Path::new(""), 2089 &[] 2090 ), 2091 Err(Box::new(ManifestErr::Package( 2092 PackageErr::InvalidWorkspaceType, 2093 PathBuf::new() 2094 ))) 2095 ); 2096 assert_eq!( 2097 Manifest::from_toml( 2098 "workspace=2\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), 2099 false, 2100 Path::new(""), 2101 &[] 2102 ), 2103 Err(Box::new(ManifestErr::Package( 2104 PackageErr::Workspace(WorkspaceErr::InvalidType, PathBuf::new()), 2105 PathBuf::new() 2106 ))) 2107 ); 2108 assert_eq!( 2109 Manifest::from_toml( 2110 "[workspace]\n[package]\nname=\"\"\nrust-version={workspace=true}".to_owned(), 2111 false, 2112 Path::new(""), 2113 &[] 2114 ), 2115 Err(Box::new(ManifestErr::Package( 2116 PackageErr::Workspace(WorkspaceErr::MissingPackage, PathBuf::new()), 2117 PathBuf::new() 2118 ))) 2119 ); 2120 assert_eq!( 2121 Manifest::from_toml( 2122 "[workspace]\npackage=2\n[package]\nname=\"\"\nrust-version={workspace=true}" 2123 .to_owned(), 2124 false, 2125 Path::new(""), 2126 &[] 2127 ), 2128 Err(Box::new(ManifestErr::Package( 2129 PackageErr::Workspace(WorkspaceErr::InvalidPackageType, PathBuf::new()), 2130 PathBuf::new() 2131 ))) 2132 ); 2133 assert_eq!( 2134 Manifest::from_toml( 2135 "[workspace.package]\n[package]\nname=\"\"\nrust-version={workspace=true}" 2136 .to_owned(), 2137 false, 2138 Path::new(""), 2139 &[] 2140 ), 2141 Err(Box::new(ManifestErr::Package( 2142 PackageErr::Workspace(WorkspaceErr::MissingPackageMsrv, PathBuf::new()), 2143 PathBuf::new() 2144 ))) 2145 ); 2146 assert_eq!( 2147 Manifest::from_toml( 2148 "[workspace.package]\nrust-version={}\n[package]\nname=\"\"\nrust-version={workspace=true}" 2149 .to_owned(), 2150 false, 2151 Path::new(""), 2152 &[] 2153 ), 2154 Err(Box::new(ManifestErr::Package( 2155 PackageErr::Workspace(WorkspaceErr::InvalidPackageMsrvType, PathBuf::new()), 2156 PathBuf::new() 2157 ))) 2158 ); 2159 assert_eq!( 2160 Manifest::from_toml( 2161 "[workspace.package]\nrust-version=\"\"\n[package]\nname=\"\"\nrust-version={workspace=true}" 2162 .to_owned(), 2163 false, 2164 Path::new(""), 2165 &[] 2166 ), 2167 Err(Box::new(ManifestErr::Package( 2168 PackageErr::Workspace(WorkspaceErr::Msrv, PathBuf::new()), 2169 PathBuf::new() 2170 ))) 2171 ); 2172 assert_eq!( 2173 Manifest::from_toml( 2174 "features=2\n[package]\nname=\"\"".to_owned(), 2175 false, 2176 Path::new(""), 2177 &[] 2178 ), 2179 Err(Box::new(ManifestErr::Features( 2180 FeaturesErr::InvalidType, 2181 PathBuf::new() 2182 ))) 2183 ); 2184 assert_eq!( 2185 Manifest::from_toml( 2186 "[features]\n\"/\"=[]\n[package]\nname=\"\"".to_owned(), 2187 false, 2188 Path::new(""), 2189 &[] 2190 ), 2191 Err(Box::new(ManifestErr::Features( 2192 FeaturesErr::InvalidName("/".to_owned()), 2193 PathBuf::new() 2194 ))) 2195 ); 2196 assert_eq!( 2197 Manifest::from_toml( 2198 "[features]\n\"dep:\"=[]\n[package]\nname=\"\"".to_owned(), 2199 false, 2200 Path::new(""), 2201 &[] 2202 ), 2203 Err(Box::new(ManifestErr::Features( 2204 FeaturesErr::InvalidName("dep:".to_owned()), 2205 PathBuf::new() 2206 ))) 2207 ); 2208 assert_eq!( 2209 Manifest::from_toml( 2210 "[features]\n\"\"=2\n[package]\nname=\"\"".to_owned(), 2211 false, 2212 Path::new(""), 2213 &[] 2214 ), 2215 Err(Box::new(ManifestErr::Features( 2216 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidFeatureType( 2217 String::new() 2218 )), 2219 PathBuf::new() 2220 ))) 2221 ); 2222 assert_eq!( 2223 Manifest::from_toml( 2224 "[features]\n\"\"=[true]\n[package]\nname=\"\"".to_owned(), 2225 false, 2226 Path::new(""), 2227 &[] 2228 ), 2229 Err(Box::new(ManifestErr::Features( 2230 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependencyType( 2231 String::new() 2232 )), 2233 PathBuf::new() 2234 ))) 2235 ); 2236 assert_eq!( 2237 Manifest::from_toml( 2238 "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), 2239 false, 2240 Path::new(""), 2241 &[] 2242 ), 2243 Err(Box::new(ManifestErr::Features( 2244 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( 2245 String::new(), 2246 "foo".to_owned() 2247 )), 2248 PathBuf::new() 2249 ))) 2250 ); 2251 // Feature dependencies can't be implied features when implied features are forbidden. 2252 assert_eq!( 2253 Manifest::from_toml( 2254 "[dependencies]\nfoo={optional=true}\n[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"" 2255 .to_owned(), 2256 false, 2257 Path::new(""), 2258 &[] 2259 ), 2260 Err(Box::new(ManifestErr::Features( 2261 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( 2262 String::new(), 2263 "foo".to_owned() 2264 )), 2265 PathBuf::new() 2266 ))) 2267 ); 2268 assert_eq!( 2269 Manifest::from_toml( 2270 "[features]\n\"\"=[\"\"]\n[package]\nname=\"\"".to_owned(), 2271 false, 2272 Path::new(""), 2273 &[] 2274 ), 2275 Err(Box::new(ManifestErr::Features( 2276 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( 2277 String::new() 2278 )), 2279 PathBuf::new() 2280 ))) 2281 ); 2282 assert_eq!( 2283 Manifest::from_toml( 2284 "[features]\n\"\"=[\"a\"]\na=[\"\"]\n[package]\nname=\"\"".to_owned(), 2285 false, 2286 Path::new(""), 2287 &[] 2288 ), 2289 Err(Box::new(ManifestErr::Features( 2290 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( 2291 String::new() 2292 )), 2293 PathBuf::new() 2294 ))) 2295 ); 2296 assert_eq!( 2297 Manifest::from_toml( 2298 "[features]\n\"\"=[\"a\"]\na=[\"b\"]\nb=[\"a\"]\n[package]\nname=\"\"".to_owned(), 2299 false, 2300 Path::new(""), 2301 &[] 2302 ), 2303 Err(Box::new(ManifestErr::Features( 2304 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( 2305 "a".to_owned() 2306 )), 2307 PathBuf::new() 2308 ))) 2309 ); 2310 assert_eq!( 2311 Manifest::from_toml( 2312 "[features]\n\"\"=[\"a\"]\na=[\"c\",\"b\"]\nb=[\"a\"]\nc=[]\n[package]\nname=\"\"" 2313 .to_owned(), 2314 false, 2315 Path::new(""), 2316 &[] 2317 ), 2318 Err(Box::new(ManifestErr::Features( 2319 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( 2320 "a".to_owned() 2321 )), 2322 PathBuf::new() 2323 ))) 2324 ); 2325 assert_eq!( 2326 Manifest::from_toml( 2327 "[features]\n\"\"=[]\na=[\"c\",\"b\"]\nb=[\"a\"]\nc=[]\n[package]\nname=\"\"" 2328 .to_owned(), 2329 false, 2330 Path::new(""), 2331 &[] 2332 ), 2333 Err(Box::new(ManifestErr::Features( 2334 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::CyclicFeature( 2335 "a".to_owned() 2336 )), 2337 PathBuf::new() 2338 ))) 2339 ); 2340 assert_eq!( 2341 Manifest::from_toml( 2342 "[features]\n\"\"=[\"a\",\"b\"]\na=[\"b\"]\nb=[]\n[package]\nname=\"\"".to_owned(), 2343 false, 2344 Path::new(""), 2345 &[] 2346 ), 2347 Err(Box::new(ManifestErr::Features( 2348 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( 2349 String::new(), 2350 "b".to_owned() 2351 )), 2352 PathBuf::new() 2353 ))) 2354 ); 2355 assert_eq!( 2356 Manifest::from_toml( 2357 "[features]\n\"\"=[\"a\",\"a\"]\na=[]\n[package]\nname=\"\"".to_owned(), 2358 false, 2359 Path::new(""), 2360 &[] 2361 ), 2362 Err(Box::new(ManifestErr::Features( 2363 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( 2364 String::new(), 2365 "a".to_owned() 2366 )), 2367 PathBuf::new() 2368 ))) 2369 ); 2370 // Duplicate `"dep:"` feature dependencies error. 2371 assert_eq!( 2372 Manifest::from_toml( 2373 "[features]\n\"\"=[\"dep:\",\"dep:\"]\na=[]\n[package]\nname=\"\"".to_owned(), 2374 false, 2375 Path::new(""), 2376 &[] 2377 ), 2378 Err(Box::new(ManifestErr::Features( 2379 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::RedundantDependency( 2380 String::new(), 2381 "dep:".to_owned() 2382 )), 2383 PathBuf::new() 2384 ))) 2385 ); 2386 assert_eq!( 2387 Manifest::from_toml( 2388 "target=2\n[package]\nname=\"\"".to_owned(), 2389 false, 2390 Path::new(""), 2391 &[] 2392 ), 2393 Err(Box::new(ManifestErr::ImpliedFeatures( 2394 ImpliedFeaturesErr::TargetType, 2395 PathBuf::new() 2396 ))) 2397 ); 2398 assert_eq!( 2399 Manifest::from_toml( 2400 "dependencies=2\n[package]\nname=\"\"".to_owned(), 2401 false, 2402 Path::new(""), 2403 &[] 2404 ), 2405 Err(Box::new(ManifestErr::ImpliedFeatures( 2406 ImpliedFeaturesErr::Dependencies(DependenciesErr::Type("dependencies")), 2407 PathBuf::new() 2408 ))) 2409 ); 2410 assert_eq!( 2411 Manifest::from_toml( 2412 "build-dependencies=2\n[package]\nname=\"\"".to_owned(), 2413 false, 2414 Path::new(""), 2415 &[] 2416 ), 2417 Err(Box::new(ManifestErr::ImpliedFeatures( 2418 ImpliedFeaturesErr::Dependencies(DependenciesErr::Type("build-dependencies")), 2419 PathBuf::new(), 2420 ))) 2421 ); 2422 assert_eq!( 2423 Manifest::from_toml( 2424 "[dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), 2425 false, 2426 Path::new(""), 2427 &[] 2428 ), 2429 Err(Box::new(ManifestErr::ImpliedFeatures( 2430 ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( 2431 "dependencies", 2432 "dep:".to_owned() 2433 )), 2434 PathBuf::new(), 2435 ))) 2436 ); 2437 assert_eq!( 2438 Manifest::from_toml( 2439 "[dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), 2440 false, 2441 Path::new(""), 2442 &[] 2443 ), 2444 Err(Box::new(ManifestErr::ImpliedFeatures( 2445 ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( 2446 "dependencies", 2447 "/".to_owned() 2448 )), 2449 PathBuf::new(), 2450 ))) 2451 ); 2452 assert_eq!( 2453 Manifest::from_toml( 2454 "[build-dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), 2455 false, 2456 Path::new(""), 2457 &[] 2458 ), 2459 Err(Box::new(ManifestErr::ImpliedFeatures( 2460 ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( 2461 "build-dependencies", 2462 "dep:".to_owned() 2463 )), 2464 PathBuf::new(), 2465 ))) 2466 ); 2467 assert_eq!( 2468 Manifest::from_toml( 2469 "[build-dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), 2470 false, 2471 Path::new(""), 2472 &[] 2473 ), 2474 Err(Box::new(ManifestErr::ImpliedFeatures( 2475 ImpliedFeaturesErr::Dependencies(DependenciesErr::Name( 2476 "build-dependencies", 2477 "/".to_owned() 2478 )), 2479 PathBuf::new(), 2480 ))) 2481 ); 2482 assert_eq!( 2483 Manifest::from_toml( 2484 "[dependencies]\n\"\"=2\n[package]\nname=\"\"".to_owned(), 2485 false, 2486 Path::new(""), 2487 &[] 2488 ), 2489 Err(Box::new(ManifestErr::ImpliedFeatures( 2490 ImpliedFeaturesErr::Dependencies(DependenciesErr::DependencyType( 2491 "dependencies", 2492 String::new() 2493 )), 2494 PathBuf::new(), 2495 ))) 2496 ); 2497 assert_eq!( 2498 Manifest::from_toml( 2499 "[build-dependencies]\n\"\"=2\n[package]\nname=\"\"".to_owned(), 2500 false, 2501 Path::new(""), 2502 &[] 2503 ), 2504 Err(Box::new(ManifestErr::ImpliedFeatures( 2505 ImpliedFeaturesErr::Dependencies(DependenciesErr::DependencyType( 2506 "build-dependencies", 2507 String::new() 2508 )), 2509 PathBuf::new(), 2510 ))) 2511 ); 2512 assert_eq!( 2513 Manifest::from_toml( 2514 "[dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), 2515 false, 2516 Path::new(""), 2517 &[] 2518 ), 2519 Err(Box::new(ManifestErr::ImpliedFeatures( 2520 ImpliedFeaturesErr::Dependencies(DependenciesErr::OptionalType( 2521 "dependencies", 2522 String::new() 2523 )), 2524 PathBuf::new(), 2525 ))) 2526 ); 2527 assert_eq!( 2528 Manifest::from_toml( 2529 "[build-dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), 2530 false, 2531 Path::new(""), 2532 &[] 2533 ), 2534 Err(Box::new(ManifestErr::ImpliedFeatures( 2535 ImpliedFeaturesErr::Dependencies(DependenciesErr::OptionalType( 2536 "build-dependencies", 2537 String::new() 2538 )), 2539 PathBuf::new(), 2540 ))) 2541 ); 2542 // Implied features are disallowed iff `!allow_implied_features`. 2543 assert_eq!( 2544 Manifest::from_toml( 2545 "[dependencies]\nfoo={optional=true}\n[package]\nname=\"\"".to_owned(), 2546 false, 2547 Path::new(""), 2548 &[] 2549 ), 2550 Err(Box::new(ManifestErr::ImpliedFeatures( 2551 ImpliedFeaturesErr::Dependencies(DependenciesErr::ImpliedFeature( 2552 "dependencies", 2553 "foo".to_owned() 2554 )), 2555 PathBuf::new(), 2556 ))) 2557 ); 2558 assert_eq!( 2559 Manifest::from_toml( 2560 "[target]\n\"\"=2\n[package]\nname=\"\"".to_owned(), 2561 false, 2562 Path::new(""), 2563 &[] 2564 ), 2565 Err(Box::new(ManifestErr::ImpliedFeatures( 2566 ImpliedFeaturesErr::TargetPlatformType(String::new()), 2567 PathBuf::new(), 2568 ))) 2569 ); 2570 assert_eq!( 2571 Manifest::from_toml( 2572 "[target.\"\"]\ndependencies=2\n[package]\nname=\"\"".to_owned(), 2573 false, 2574 Path::new(""), 2575 &[] 2576 ), 2577 Err(Box::new(ManifestErr::ImpliedFeatures( 2578 ImpliedFeaturesErr::TagetPlatformDependencies( 2579 String::new(), 2580 DependenciesErr::Type("dependencies") 2581 ), 2582 PathBuf::new(), 2583 ))) 2584 ); 2585 assert_eq!( 2586 Manifest::from_toml( 2587 "[target.\"\"]\nbuild-dependencies=2\n[package]\nname=\"\"".to_owned(), 2588 false, 2589 Path::new(""), 2590 &[] 2591 ), 2592 Err(Box::new(ManifestErr::ImpliedFeatures( 2593 ImpliedFeaturesErr::TagetPlatformDependencies( 2594 String::new(), 2595 DependenciesErr::Type("build-dependencies") 2596 ), 2597 PathBuf::new(), 2598 ))) 2599 ); 2600 assert_eq!( 2601 Manifest::from_toml( 2602 "[target.\"\".dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), 2603 false, 2604 Path::new(""), 2605 &[] 2606 ), 2607 Err(Box::new(ManifestErr::ImpliedFeatures( 2608 ImpliedFeaturesErr::TagetPlatformDependencies( 2609 String::new(), 2610 DependenciesErr::Name("dependencies", "/".to_owned()) 2611 ), 2612 PathBuf::new(), 2613 ))) 2614 ); 2615 assert_eq!( 2616 Manifest::from_toml( 2617 "[target.\"\".dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), 2618 false, 2619 Path::new(""), 2620 &[] 2621 ), 2622 Err(Box::new(ManifestErr::ImpliedFeatures( 2623 ImpliedFeaturesErr::TagetPlatformDependencies( 2624 String::new(), 2625 DependenciesErr::Name("dependencies", "dep:".to_owned()) 2626 ), 2627 PathBuf::new(), 2628 ))) 2629 ); 2630 assert_eq!( 2631 Manifest::from_toml( 2632 "[target.\"\".build-dependencies]\n\"/\"=\"\"\n[package]\nname=\"\"".to_owned(), 2633 false, 2634 Path::new(""), 2635 &[] 2636 ), 2637 Err(Box::new(ManifestErr::ImpliedFeatures( 2638 ImpliedFeaturesErr::TagetPlatformDependencies( 2639 String::new(), 2640 DependenciesErr::Name("build-dependencies", "/".to_owned()) 2641 ), 2642 PathBuf::new(), 2643 ))) 2644 ); 2645 assert_eq!( 2646 Manifest::from_toml( 2647 "[target.\"\".build-dependencies]\n\"dep:\"=\"\"\n[package]\nname=\"\"".to_owned(), 2648 false, 2649 Path::new(""), 2650 &[] 2651 ), 2652 Err(Box::new(ManifestErr::ImpliedFeatures( 2653 ImpliedFeaturesErr::TagetPlatformDependencies( 2654 String::new(), 2655 DependenciesErr::Name("build-dependencies", "dep:".to_owned()) 2656 ), 2657 PathBuf::new(), 2658 ))) 2659 ); 2660 assert_eq!( 2661 Manifest::from_toml( 2662 "[target.\"\".dependencies]\n\"\"=false\n[package]\nname=\"\"".to_owned(), 2663 false, 2664 Path::new(""), 2665 &[] 2666 ), 2667 Err(Box::new(ManifestErr::ImpliedFeatures( 2668 ImpliedFeaturesErr::TagetPlatformDependencies( 2669 String::new(), 2670 DependenciesErr::DependencyType("dependencies", String::new()) 2671 ), 2672 PathBuf::new(), 2673 ))) 2674 ); 2675 assert_eq!( 2676 Manifest::from_toml( 2677 "[target.\"\".build-dependencies]\n\"\"=false\n[package]\nname=\"\"".to_owned(), 2678 false, 2679 Path::new(""), 2680 &[] 2681 ), 2682 Err(Box::new(ManifestErr::ImpliedFeatures( 2683 ImpliedFeaturesErr::TagetPlatformDependencies( 2684 String::new(), 2685 DependenciesErr::DependencyType("build-dependencies", String::new()) 2686 ), 2687 PathBuf::new(), 2688 ))) 2689 ); 2690 assert_eq!( 2691 Manifest::from_toml( 2692 "[target.\"\".dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"".to_owned(), 2693 false, 2694 Path::new(""), 2695 &[] 2696 ), 2697 Err(Box::new(ManifestErr::ImpliedFeatures( 2698 ImpliedFeaturesErr::TagetPlatformDependencies( 2699 String::new(), 2700 DependenciesErr::OptionalType("dependencies", String::new()) 2701 ), 2702 PathBuf::new(), 2703 ))) 2704 ); 2705 assert_eq!( 2706 Manifest::from_toml( 2707 "[target.\"\".build-dependencies]\n\"\"={optional=2}\n[package]\nname=\"\"" 2708 .to_owned(), 2709 false, 2710 Path::new(""), 2711 &[] 2712 ), 2713 Err(Box::new(ManifestErr::ImpliedFeatures( 2714 ImpliedFeaturesErr::TagetPlatformDependencies( 2715 String::new(), 2716 DependenciesErr::OptionalType("build-dependencies", String::new()) 2717 ), 2718 PathBuf::new(), 2719 ))) 2720 ); 2721 // An invalid dependency error occurs later when we `allow_implied_features` since 2722 // implied features aren't added until after feature extraction. 2723 assert_eq!( 2724 Manifest::from_toml( 2725 "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), 2726 true, 2727 Path::new(""), 2728 &[] 2729 ), 2730 Err(Box::new(ManifestErr::ImpliedFeatures( 2731 ImpliedFeaturesErr::InvalidDependency(String::new(), "foo".to_owned()), 2732 PathBuf::new(), 2733 ))) 2734 ); 2735 // In contrast, above would have erred sooner if `!allow_implied_features`. 2736 assert_eq!( 2737 Manifest::from_toml( 2738 "[features]\n\"\"=[\"foo\"]\n[package]\nname=\"\"".to_owned(), 2739 false, 2740 Path::new(""), 2741 &[] 2742 ), 2743 Err(Box::new(ManifestErr::Features( 2744 FeaturesErr::FeatureDependencies(FeatureDependenciesErr::InvalidDependency( 2745 String::new(), 2746 "foo".to_owned() 2747 )), 2748 PathBuf::new() 2749 ))) 2750 ); 2751 assert_eq!( 2752 Manifest::from_toml( 2753 "[package]\nname=\"\"".to_owned(), 2754 false, 2755 Path::new(""), 2756 &["a".to_owned()] 2757 ), 2758 Err(Box::new(ManifestErr::UndefinedIgnoreFeature( 2759 "a".to_owned(), 2760 PathBuf::new() 2761 ))) 2762 ); 2763 // Even if we forbid implied features, we don't error when a feature is defined 2764 // with the same name of an implied feature. This is due to simplicity in code 2765 // and the fact that `cargo` will error anyway. 2766 // 2767 // For example once `cargo` is invoked, an error will occur due to duplicate features: 2768 // the explict feature `foo` and the implied feature from the dependency `foo`. 2769 assert_eq!( 2770 Manifest::from_toml( 2771 "[dependencies]\nfoo={optional=true}\n[features]\nfoo=[]\n[package]\nname=\"\"" 2772 .to_owned(), 2773 false, 2774 Path::new(""), 2775 &[] 2776 ), 2777 Ok(Manifest { 2778 package: Package { 2779 msrv: None, 2780 name: String::new(), 2781 }, 2782 features: Features(vec![("foo".to_owned(), Vec::new())]), 2783 }) 2784 ); 2785 // Allow major-only MSRV. 2786 assert_eq!( 2787 Manifest::from_toml( 2788 "[package]\nname=\"foo\"\nrust-version=\"0\"".to_owned(), 2789 false, 2790 Path::new(""), 2791 &[] 2792 ), 2793 Ok(Manifest { 2794 package: Package { 2795 msrv: Some(Msrv { 2796 major: 0, 2797 minor: None, 2798 patch: None, 2799 }), 2800 name: "foo".to_owned(), 2801 }, 2802 features: Features(Vec::new()), 2803 }) 2804 ); 2805 // Allow escapes. 2806 assert_eq!( 2807 Manifest::from_toml( 2808 "[\"\\u0070ackage\"]\n\"n\\u0061me\"=\"\\u0066oo\"\n\"\\u0072ust-version\"=\"0\\u002E\\u0031\"".to_owned(), 2809 false, 2810 Path::new(""), 2811 &[] 2812 ), 2813 Ok(Manifest { 2814 package: Package { 2815 msrv: Some(Msrv { 2816 major: 0, 2817 minor: Some(1), 2818 patch: None, 2819 }), 2820 name: "foo".to_owned(), 2821 }, 2822 features: Features(Vec::new()), 2823 }) 2824 ); 2825 assert_eq!( 2826 Manifest::from_toml( 2827 "[package]\nname=\"\"\nrust-version=\"0.0.0\"".to_owned(), 2828 false, 2829 Path::new(""), 2830 &[] 2831 ), 2832 Ok(Manifest { 2833 package: Package { 2834 msrv: Some(Msrv { 2835 major: 0, 2836 minor: Some(0), 2837 patch: Some(0), 2838 }), 2839 name: String::new(), 2840 }, 2841 features: Features(Vec::new()), 2842 }) 2843 ); 2844 // Ignore non `rust-version` keys in `package`. Ignore keys in the root document except `package`, 2845 // `features`, `dependencies`, `build-dependencies`, and `target`. Ignore keys in 2846 // `target.<something>` unless the key is `dependencies` or `build-dependencies`. Don't treat 2847 // `<something>` special in `target.<something>` other than its being a table. 2848 assert_eq!( 2849 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(""), &[]), 2850 Ok(Manifest { 2851 package: Package { 2852 msrv: Some(Msrv { 2853 major: u64::MAX, 2854 minor: Some(u64::MAX), 2855 patch: Some(u64::MAX), 2856 }), 2857 name: String::new(), 2858 }, 2859 features: Features(Vec::new()), 2860 }) 2861 ); 2862 // [package] 2863 // name = "" 2864 // 2865 // ["\u0064ependencies"] 2866 // "\u0000" = "\u0000" 2867 // a = { optional = true } 2868 // 2869 // ["build-\u0064ependencies"] 2870 // "\u0000" = { optional = true } 2871 // 2872 // [dev-dependencies] 2873 // buzz = { optional = true } 2874 // 2875 // [target."".dependencies] 2876 // b = { optional = false, foo = 2 } 2877 // fizz = { optional = true, foo = 3 } 2878 // 2879 // [target.a.dependencies] 2880 // c = { optional = true } 2881 // wuzz = { optional = true } 2882 // 2883 // [features] 2884 // default = ["bar","dep:lk","a/ak", "a/ak"] 2885 // bar = ["dep\u003Awuzz"] 2886 // 2887 // We allow any and all key names unless it's the features table or a dependency table; in which case 2888 // key names must not contain `'/'` nor begin with `"dep:"`. 2889 // 2890 // The order of features is based on the following hierarchy: 2891 // * Explict features: lexicographically sorted 2892 // * dependencies: optional only, lexicographically sorted, only if an explicit feature doesn't exist with 2893 // the same name nor any explicit feature contains a dependency named `"dep:<dependecy>"` and we allow 2894 // implied features. If such feature exists, we don't error but simply don't add. 2895 // * build-dependencies: read above. 2896 // * target.<something>: lexicographically sorted by something, within `something`, `dependencies` 2897 // is first using the same methodology as item 2, last `build-dependencies`. 2898 // 2899 // Once the order of features is determined, the only feature dependencies that are retained are those 2900 // that don't contain `'/'` nor begin with `"dep:"`. We don't require dependencies to be defined for 2901 // feature dependencies that contain `'/'` or begin with `"dep:"`. We don't care about duplicate feature 2902 // dependencies that contain `'/'`. 2903 // 2904 // Based on above, `Features` looks like the following: 2905 // 1. (bar, []) 2906 // 2. (default, ["bar"]) 2907 // 3. (a, []) 2908 // 4. (\x00, []) 2909 // 5. (fizz, []) 2910 // 6. (c, []) 2911 assert_eq!( 2912 Manifest::from_toml( 2913 "[\"\\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(), 2914 true, 2915 Path::new(""), 2916 &[] 2917 ), 2918 Ok(Manifest { 2919 package: Package { 2920 msrv: None, 2921 name: String::new(), 2922 }, 2923 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())]), 2924 }) 2925 ); 2926 // [package] 2927 // name = "" 2928 // 2929 // [dependencies] 2930 // foo = { "optional" = true } 2931 // fizz = { "optional" = true } 2932 // 2933 // [features] 2934 // fizz = ["dep:fizz"] 2935 // bar = ["dep:foo"] 2936 assert_eq!( 2937 Manifest::from_toml( 2938 "[package]\nname=\"\"\n[dependencies]\nfoo={optional=true}\nfizz={optional=true}\n[features]\nfizz=[\"dep:fizz\"]\nbar=[\"dep:foo\"]".to_owned(), 2939 false, 2940 Path::new(""), 2941 &[] 2942 ), 2943 Ok(Manifest { 2944 package: Package { 2945 msrv: None, 2946 name: String::new(), 2947 }, 2948 features: Features(vec![ 2949 ("bar".to_owned(), Vec::new()), 2950 ("fizz".to_owned(), Vec::new()) 2951 ]), 2952 }) 2953 ); 2954 // [package] 2955 // name = "" 2956 // 2957 // [dependencies] 2958 // bar = { "optional" = true } 2959 // 2960 // [features] 2961 // foo = ["bar"] 2962 assert_eq!( 2963 Manifest::from_toml( 2964 "[package]\nname=\"\"\n[dependencies]\nbar={optional=true}\n[features]\nfoo=[\"bar\"]" 2965 .to_owned(), 2966 true, 2967 Path::new(""), 2968 &[] 2969 ), 2970 Ok(Manifest { 2971 package: Package { 2972 msrv: None, 2973 name: String::new(), 2974 }, 2975 features: Features(vec![ 2976 ("foo".to_owned(), vec!["bar".to_owned()]), 2977 ("bar".to_owned(), Vec::new()), 2978 ]), 2979 }) 2980 ); 2981 assert_eq!( 2982 Manifest::from_toml( 2983 "[package]\nname=\"\"\n[features]\na=[]\nb=[\"a\"]".to_owned(), 2984 false, 2985 Path::new(""), 2986 &["a".to_owned()] 2987 ), 2988 Ok(Manifest { 2989 package: Package { 2990 msrv: None, 2991 name: String::new(), 2992 }, 2993 features: Features(vec![]), 2994 }) 2995 ); 2996 assert_eq!( 2997 Manifest::from_toml( 2998 "[package]\nname=\"\"\n[features]\na=[]\nb=[\"a\"]".to_owned(), 2999 false, 3000 Path::new(""), 3001 &["b".to_owned()] 3002 ), 3003 Ok(Manifest { 3004 package: Package { 3005 msrv: None, 3006 name: String::new(), 3007 }, 3008 features: Features(vec![("a".to_owned(), Vec::new())]), 3009 }) 3010 ); 3011 assert_eq!( 3012 Manifest::from_toml( 3013 "[package]\nname=\"\"\n[dependencies]\nc={optional=true}\n[features]\nb=[\"c\"]" 3014 .to_owned(), 3015 true, 3016 Path::new(""), 3017 &["c".to_owned()] 3018 ), 3019 Ok(Manifest { 3020 package: Package { 3021 msrv: None, 3022 name: String::new(), 3023 }, 3024 features: Features(vec![]), 3025 }) 3026 ); 3027 assert_eq!( 3028 Manifest::from_toml( 3029 "[package]\nname=\"\"\n[dependencies]\nc={optional=true}\n[features]\nb=[\"c\"]" 3030 .to_owned(), 3031 true, 3032 Path::new(""), 3033 &["b".to_owned()] 3034 ), 3035 Ok(Manifest { 3036 package: Package { 3037 msrv: None, 3038 name: String::new(), 3039 }, 3040 features: Features(vec![("c".to_owned(), Vec::new())]), 3041 }) 3042 ); 3043 } 3044 #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] 3045 #[expect( 3046 clippy::cognitive_complexity, 3047 clippy::too_many_lines, 3048 reason = "want to test for a lot of things" 3049 )] 3050 #[test] 3051 fn power_set() { 3052 #[cfg(target_pointer_width = "16")] 3053 let feat_len_one_too_large = 17; 3054 #[cfg(target_pointer_width = "32")] 3055 let feat_len_one_too_large = 33; 3056 #[cfg(target_pointer_width = "64")] 3057 let feat_len_one_too_large = 65; 3058 let mut feats = Features(vec![(String::new(), Vec::new()); feat_len_one_too_large]); 3059 assert_eq!(PowerSet::new(&feats, false), Err(TooManyFeaturesErr)); 3060 #[cfg(target_pointer_width = "16")] 3061 let max_feat_len = 16; 3062 #[cfg(target_pointer_width = "32")] 3063 let max_feat_len = 32; 3064 #[cfg(target_pointer_width = "64")] 3065 let max_feat_len = 64; 3066 feats.0 = vec![(String::new(), Vec::new()); max_feat_len]; 3067 #[cfg(any( 3068 target_pointer_width = "16", 3069 target_pointer_width = "32", 3070 target_pointer_width = "64" 3071 ))] 3072 assert_eq!( 3073 PowerSet::new(&feats, false), 3074 Ok(Some(PowerSet { 3075 feats: feats.0.as_slice(), 3076 has_remaining: true, 3077 check_overlap: false, 3078 idx: usize::MAX, 3079 buffer: vec![""; max_feat_len], 3080 set: String::new(), 3081 skipped_sets_counter: 0, 3082 skip_empty_set: false, 3083 })) 3084 ); 3085 feats.0 = Vec::new(); 3086 assert_eq!( 3087 PowerSet::new(&feats, false), 3088 Ok(Some(PowerSet { 3089 feats: feats.0.as_slice(), 3090 has_remaining: true, 3091 check_overlap: false, 3092 idx: 0, 3093 buffer: Vec::new(), 3094 set: String::new(), 3095 skipped_sets_counter: 0, 3096 skip_empty_set: false, 3097 })) 3098 ); 3099 assert_eq!(PowerSet::new(&feats, true), Ok(None)); 3100 let mut power_set = PowerSet::new(&feats, false) 3101 .unwrap_or_else(|_e| { 3102 unreachable!("not possible since we just verified PowerSet::new returned Ok") 3103 }) 3104 .unwrap_or_else(|| { 3105 unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") 3106 }); 3107 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); 3108 assert_eq!(power_set.next_set(), Some("")); 3109 assert_eq!( 3110 power_set, 3111 PowerSet { 3112 feats: feats.0.as_slice(), 3113 has_remaining: false, 3114 check_overlap: false, 3115 idx: 0, 3116 buffer: Vec::new(), 3117 set: String::new(), 3118 skipped_sets_counter: 0, 3119 skip_empty_set: false 3120 } 3121 ); 3122 assert_eq!(power_set.next_set(), None); 3123 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); 3124 assert_eq!( 3125 power_set, 3126 PowerSet { 3127 feats: feats.0.as_slice(), 3128 has_remaining: false, 3129 check_overlap: false, 3130 idx: 0, 3131 buffer: Vec::new(), 3132 set: String::new(), 3133 skipped_sets_counter: 0, 3134 skip_empty_set: false 3135 } 3136 ); 3137 assert_eq!(power_set.next_set(), None); 3138 power_set.reset(); 3139 assert_eq!( 3140 power_set, 3141 PowerSet { 3142 feats: feats.0.as_slice(), 3143 has_remaining: true, 3144 check_overlap: false, 3145 idx: 0, 3146 buffer: Vec::new(), 3147 set: String::new(), 3148 skipped_sets_counter: 0, 3149 skip_empty_set: false 3150 } 3151 ); 3152 assert_eq!(power_set.next_set(), Some("")); 3153 assert_eq!( 3154 power_set, 3155 PowerSet { 3156 feats: feats.0.as_slice(), 3157 has_remaining: false, 3158 check_overlap: false, 3159 idx: 0, 3160 buffer: Vec::new(), 3161 set: String::new(), 3162 skipped_sets_counter: 0, 3163 skip_empty_set: false 3164 } 3165 ); 3166 assert_eq!(power_set.next_set(), None); 3167 assert_eq!( 3168 power_set, 3169 PowerSet { 3170 feats: feats.0.as_slice(), 3171 has_remaining: false, 3172 check_overlap: false, 3173 idx: 0, 3174 buffer: Vec::new(), 3175 set: String::new(), 3176 skipped_sets_counter: 0, 3177 skip_empty_set: false 3178 } 3179 ); 3180 assert_eq!(power_set.next_set(), None); 3181 // [features] 3182 // a = ["b"] 3183 // b = ["c", "d"] 3184 // c = [] 3185 // d = [] 3186 feats.0 = vec![ 3187 ("a".to_owned(), vec!["b".to_owned()]), 3188 ("b".to_owned(), vec!["c".to_owned(), "d".to_owned()]), 3189 ("c".to_owned(), Vec::new()), 3190 ("d".to_owned(), Vec::new()), 3191 ]; 3192 assert_eq!( 3193 PowerSet::new(&feats, false), 3194 Ok(Some(PowerSet { 3195 feats: feats.0.as_slice(), 3196 has_remaining: true, 3197 // At least one feature depends on another, so this will be set to `true`. 3198 check_overlap: true, 3199 idx: 15, 3200 buffer: vec!["a", "b", "c", "d"], 3201 set: String::new(), 3202 skipped_sets_counter: 0, 3203 skip_empty_set: false 3204 })) 3205 ); 3206 power_set = PowerSet::new(&feats, false) 3207 .unwrap_or_else(|_e| { 3208 unreachable!("not possible since we just verified PowerSet::new returned Ok") 3209 }) 3210 .unwrap_or_else(|| { 3211 unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") 3212 }); 3213 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); 3214 // Order is the following: 3215 // 1. a,b,c,d: skipped since a depends on b. 3216 // 2. b,c,d: skipped since b depends on c. 3217 // 3. a,c,d: skipped since a depends on c (via b). 3218 // 4. c,d 3219 // 5. a,b,d: skipped since a depends on b. 3220 // 6. b,d: skipped since b depends on d. 3221 // 7. a,d: skipped since a depends on d (via b). 3222 // 8. d 3223 // 9. a,b,c: skipped since a depends on b. 3224 // 10. b,c: skipped since b depends on c. 3225 // 11. a,c: skipped since a depends on c (via b). 3226 // 12. c 3227 // 13. a,b: skipped since a depends on b. 3228 // 14. b 3229 // 15. a 3230 // 16. 3231 assert_eq!(power_set.next_set(), Some("c,d")); 3232 assert_eq!( 3233 power_set, 3234 PowerSet { 3235 feats: feats.0.as_slice(), 3236 has_remaining: true, 3237 check_overlap: true, 3238 // We started at 15, and we iterated 4 items (skipping 3). 3239 idx: 11, 3240 buffer: vec!["c", "d"], 3241 set: "c,d".to_owned(), 3242 skipped_sets_counter: 3, 3243 skip_empty_set: false 3244 } 3245 ); 3246 assert_eq!(power_set.next_set(), Some("d")); 3247 assert_eq!( 3248 power_set, 3249 PowerSet { 3250 feats: feats.0.as_slice(), 3251 has_remaining: true, 3252 check_overlap: true, 3253 // We started at 11, and we iterated 4 items (skipping 3). 3254 idx: 7, 3255 buffer: vec!["d"], 3256 set: "d".to_owned(), 3257 skipped_sets_counter: 6, 3258 skip_empty_set: false 3259 } 3260 ); 3261 assert_eq!(power_set.next_set(), Some("c")); 3262 assert_eq!( 3263 power_set, 3264 PowerSet { 3265 feats: feats.0.as_slice(), 3266 has_remaining: true, 3267 check_overlap: true, 3268 // We started at 7, and we iterated 4 items (skipping 3). 3269 idx: 3, 3270 buffer: vec!["c"], 3271 set: "c".to_owned(), 3272 skipped_sets_counter: 9, 3273 skip_empty_set: false 3274 } 3275 ); 3276 assert_eq!(power_set.next_set(), Some("b")); 3277 assert_eq!( 3278 power_set, 3279 PowerSet { 3280 feats: feats.0.as_slice(), 3281 has_remaining: true, 3282 check_overlap: true, 3283 // We started at 3, and we iterated 2 items (skipping 1). 3284 idx: 1, 3285 buffer: vec!["b"], 3286 set: "b".to_owned(), 3287 skipped_sets_counter: 10, 3288 skip_empty_set: false 3289 } 3290 ); 3291 assert_eq!(power_set.next_set(), Some("a")); 3292 assert_eq!( 3293 power_set, 3294 PowerSet { 3295 feats: feats.0.as_slice(), 3296 has_remaining: true, 3297 check_overlap: true, 3298 // We started at 1, and we iterated 1 item. 3299 idx: 0, 3300 buffer: vec!["a"], 3301 set: "a".to_owned(), 3302 skipped_sets_counter: 10, 3303 skip_empty_set: false 3304 } 3305 ); 3306 assert_eq!(power_set.next_set(), Some("")); 3307 assert_eq!( 3308 power_set, 3309 PowerSet { 3310 feats: feats.0.as_slice(), 3311 has_remaining: false, 3312 check_overlap: true, 3313 // We started at 0, and we iterated 1 item but we don't underflow instead `has_remaining` is set 3314 // to `false`. 3315 idx: 0, 3316 buffer: Vec::new(), 3317 set: String::new(), 3318 skipped_sets_counter: 10, 3319 skip_empty_set: false 3320 } 3321 ); 3322 assert_eq!(power_set.next_set(), None); 3323 // Internal state is left unchanged. 3324 assert_eq!( 3325 power_set, 3326 PowerSet { 3327 feats: feats.0.as_slice(), 3328 has_remaining: false, 3329 check_overlap: true, 3330 idx: 0, 3331 buffer: Vec::new(), 3332 set: String::new(), 3333 skipped_sets_counter: 10, 3334 skip_empty_set: false 3335 } 3336 ); 3337 assert_eq!(power_set.next_set(), None); 3338 // Internal state is left unchanged. 3339 assert_eq!( 3340 power_set, 3341 PowerSet { 3342 feats: feats.0.as_slice(), 3343 has_remaining: false, 3344 check_overlap: true, 3345 idx: 0, 3346 buffer: Vec::new(), 3347 set: String::new(), 3348 skipped_sets_counter: 10, 3349 skip_empty_set: false 3350 } 3351 ); 3352 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); 3353 power_set.reset(); 3354 // `PowerSet::reset` only resets what is necessary nothing more; in particular, `buffer` and `set` are 3355 // left alone. 3356 assert_eq!( 3357 power_set, 3358 PowerSet { 3359 feats: feats.0.as_slice(), 3360 has_remaining: true, 3361 check_overlap: true, 3362 idx: 15, 3363 buffer: Vec::new(), 3364 set: String::new(), 3365 skipped_sets_counter: 0, 3366 skip_empty_set: false 3367 } 3368 ); 3369 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); 3370 // Same as above except no feature depends on any other. 3371 // [features] 3372 // a = [] 3373 // b = [] 3374 // c = [] 3375 // d = [] 3376 feats.0 = vec![ 3377 ("a".to_owned(), Vec::new()), 3378 ("b".to_owned(), Vec::new()), 3379 ("c".to_owned(), Vec::new()), 3380 ("d".to_owned(), Vec::new()), 3381 ]; 3382 assert_eq!( 3383 PowerSet::new(&feats, false), 3384 Ok(Some(PowerSet { 3385 feats: feats.0.as_slice(), 3386 has_remaining: true, 3387 check_overlap: false, 3388 idx: 15, 3389 buffer: vec!["a", "b", "c", "d"], 3390 set: String::new(), 3391 skipped_sets_counter: 0, 3392 skip_empty_set: false 3393 })) 3394 ); 3395 power_set = PowerSet::new(&feats, false) 3396 .unwrap_or_else(|_e| { 3397 unreachable!("not possible since we just verified PowerSet::new returned Ok") 3398 }) 3399 .unwrap_or_else(|| { 3400 unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") 3401 }); 3402 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); 3403 // Order is the same as above except nothing is skipped: 3404 // 1. a,b,c,d 3405 // 2. b,c,d 3406 // 3. a,c,d 3407 // 4. c,d 3408 // 5. a,b,d 3409 // 6. b,d 3410 // 7. a,d 3411 // 8. d 3412 // 9. a,b,c 3413 // 10. b,c 3414 // 11. a,c 3415 // 12. c 3416 // 13. a,b 3417 // 14. b 3418 // 15. a 3419 // 16. 3420 assert_eq!(power_set.next_set(), Some("a,b,c,d")); 3421 assert_eq!( 3422 power_set, 3423 PowerSet { 3424 feats: feats.0.as_slice(), 3425 has_remaining: true, 3426 check_overlap: false, 3427 idx: 14, 3428 buffer: vec!["a", "b", "c", "d"], 3429 set: "a,b,c,d".to_owned(), 3430 skipped_sets_counter: 0, 3431 skip_empty_set: false 3432 } 3433 ); 3434 assert_eq!(power_set.next_set(), Some("b,c,d")); 3435 assert_eq!( 3436 power_set, 3437 PowerSet { 3438 feats: feats.0.as_slice(), 3439 has_remaining: true, 3440 check_overlap: false, 3441 idx: 13, 3442 buffer: vec!["b", "c", "d"], 3443 set: "b,c,d".to_owned(), 3444 skipped_sets_counter: 0, 3445 skip_empty_set: false 3446 } 3447 ); 3448 assert_eq!(power_set.next_set(), Some("a,c,d")); 3449 assert_eq!( 3450 power_set, 3451 PowerSet { 3452 feats: feats.0.as_slice(), 3453 has_remaining: true, 3454 check_overlap: false, 3455 idx: 12, 3456 buffer: vec!["a", "c", "d"], 3457 set: "a,c,d".to_owned(), 3458 skipped_sets_counter: 0, 3459 skip_empty_set: false 3460 } 3461 ); 3462 assert_eq!(power_set.next_set(), Some("c,d")); 3463 assert_eq!( 3464 power_set, 3465 PowerSet { 3466 feats: feats.0.as_slice(), 3467 has_remaining: true, 3468 check_overlap: false, 3469 idx: 11, 3470 buffer: vec!["c", "d"], 3471 set: "c,d".to_owned(), 3472 skipped_sets_counter: 0, 3473 skip_empty_set: false 3474 } 3475 ); 3476 assert_eq!(power_set.next_set(), Some("a,b,d")); 3477 assert_eq!( 3478 power_set, 3479 PowerSet { 3480 feats: feats.0.as_slice(), 3481 has_remaining: true, 3482 check_overlap: false, 3483 idx: 10, 3484 buffer: vec!["a", "b", "d"], 3485 set: "a,b,d".to_owned(), 3486 skipped_sets_counter: 0, 3487 skip_empty_set: false 3488 } 3489 ); 3490 assert_eq!(power_set.next_set(), Some("b,d")); 3491 assert_eq!( 3492 power_set, 3493 PowerSet { 3494 feats: feats.0.as_slice(), 3495 has_remaining: true, 3496 check_overlap: false, 3497 idx: 9, 3498 buffer: vec!["b", "d"], 3499 set: "b,d".to_owned(), 3500 skipped_sets_counter: 0, 3501 skip_empty_set: false 3502 } 3503 ); 3504 assert_eq!(power_set.next_set(), Some("a,d")); 3505 assert_eq!( 3506 power_set, 3507 PowerSet { 3508 feats: feats.0.as_slice(), 3509 has_remaining: true, 3510 check_overlap: false, 3511 idx: 8, 3512 buffer: vec!["a", "d"], 3513 set: "a,d".to_owned(), 3514 skipped_sets_counter: 0, 3515 skip_empty_set: false 3516 } 3517 ); 3518 assert_eq!(power_set.next_set(), Some("d")); 3519 assert_eq!( 3520 power_set, 3521 PowerSet { 3522 feats: feats.0.as_slice(), 3523 has_remaining: true, 3524 check_overlap: false, 3525 idx: 7, 3526 buffer: vec!["d"], 3527 set: "d".to_owned(), 3528 skipped_sets_counter: 0, 3529 skip_empty_set: false 3530 } 3531 ); 3532 assert_eq!(power_set.next_set(), Some("a,b,c")); 3533 assert_eq!( 3534 power_set, 3535 PowerSet { 3536 feats: feats.0.as_slice(), 3537 has_remaining: true, 3538 check_overlap: false, 3539 idx: 6, 3540 buffer: vec!["a", "b", "c"], 3541 set: "a,b,c".to_owned(), 3542 skipped_sets_counter: 0, 3543 skip_empty_set: false 3544 } 3545 ); 3546 assert_eq!(power_set.next_set(), Some("b,c")); 3547 assert_eq!( 3548 power_set, 3549 PowerSet { 3550 feats: feats.0.as_slice(), 3551 has_remaining: true, 3552 check_overlap: false, 3553 idx: 5, 3554 buffer: vec!["b", "c"], 3555 set: "b,c".to_owned(), 3556 skipped_sets_counter: 0, 3557 skip_empty_set: false 3558 } 3559 ); 3560 assert_eq!(power_set.next_set(), Some("a,c")); 3561 assert_eq!( 3562 power_set, 3563 PowerSet { 3564 feats: feats.0.as_slice(), 3565 has_remaining: true, 3566 check_overlap: false, 3567 idx: 4, 3568 buffer: vec!["a", "c"], 3569 set: "a,c".to_owned(), 3570 skipped_sets_counter: 0, 3571 skip_empty_set: false 3572 } 3573 ); 3574 assert_eq!(power_set.next_set(), Some("c")); 3575 assert_eq!( 3576 power_set, 3577 PowerSet { 3578 feats: feats.0.as_slice(), 3579 has_remaining: true, 3580 check_overlap: false, 3581 idx: 3, 3582 buffer: vec!["c"], 3583 set: "c".to_owned(), 3584 skipped_sets_counter: 0, 3585 skip_empty_set: false 3586 } 3587 ); 3588 assert_eq!(power_set.next_set(), Some("a,b")); 3589 assert_eq!( 3590 power_set, 3591 PowerSet { 3592 feats: feats.0.as_slice(), 3593 has_remaining: true, 3594 check_overlap: false, 3595 idx: 2, 3596 buffer: vec!["a", "b"], 3597 set: "a,b".to_owned(), 3598 skipped_sets_counter: 0, 3599 skip_empty_set: false 3600 } 3601 ); 3602 assert_eq!(power_set.next_set(), Some("b")); 3603 assert_eq!( 3604 power_set, 3605 PowerSet { 3606 feats: feats.0.as_slice(), 3607 has_remaining: true, 3608 check_overlap: false, 3609 idx: 1, 3610 buffer: vec!["b"], 3611 set: "b".to_owned(), 3612 skipped_sets_counter: 0, 3613 skip_empty_set: false 3614 } 3615 ); 3616 assert_eq!(power_set.next_set(), Some("a")); 3617 assert_eq!( 3618 power_set, 3619 PowerSet { 3620 feats: feats.0.as_slice(), 3621 has_remaining: true, 3622 check_overlap: false, 3623 idx: 0, 3624 buffer: vec!["a"], 3625 set: "a".to_owned(), 3626 skipped_sets_counter: 0, 3627 skip_empty_set: false 3628 } 3629 ); 3630 assert_eq!(power_set.next_set(), Some("")); 3631 assert_eq!( 3632 power_set, 3633 PowerSet { 3634 feats: feats.0.as_slice(), 3635 has_remaining: false, 3636 check_overlap: false, 3637 idx: 0, 3638 buffer: Vec::new(), 3639 set: String::new(), 3640 skipped_sets_counter: 0, 3641 skip_empty_set: false 3642 } 3643 ); 3644 assert_eq!(power_set.next_set(), None); 3645 assert_eq!( 3646 power_set, 3647 PowerSet { 3648 feats: feats.0.as_slice(), 3649 has_remaining: false, 3650 check_overlap: false, 3651 idx: 0, 3652 buffer: Vec::new(), 3653 set: String::new(), 3654 skipped_sets_counter: 0, 3655 skip_empty_set: false 3656 } 3657 ); 3658 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(16)); 3659 feats.0 = vec![("a".to_owned(), Vec::new())]; 3660 assert_eq!( 3661 PowerSet::new(&feats, true), 3662 Ok(Some(PowerSet { 3663 feats: feats.0.as_slice(), 3664 has_remaining: true, 3665 check_overlap: false, 3666 idx: 1, 3667 buffer: vec!["a"], 3668 set: String::new(), 3669 skipped_sets_counter: 0, 3670 skip_empty_set: true 3671 })) 3672 ); 3673 power_set = PowerSet::new(&feats, true) 3674 .unwrap_or_else(|_e| { 3675 unreachable!("not possible since we just verified PowerSet::new returned Ok") 3676 }) 3677 .unwrap_or_else(|| { 3678 unreachable!("not possible since we just verified PowerSet::new returned Ok(Some)") 3679 }); 3680 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); 3681 assert_eq!(power_set.next_set(), Some("a")); 3682 assert_eq!( 3683 power_set, 3684 PowerSet { 3685 feats: feats.0.as_slice(), 3686 has_remaining: false, 3687 check_overlap: false, 3688 idx: 0, 3689 buffer: vec!["a"], 3690 set: "a".to_owned(), 3691 skipped_sets_counter: 0, 3692 skip_empty_set: true, 3693 } 3694 ); 3695 assert_eq!(power_set.next_set(), None); 3696 assert_eq!( 3697 power_set, 3698 PowerSet { 3699 feats: feats.0.as_slice(), 3700 has_remaining: false, 3701 check_overlap: false, 3702 idx: 0, 3703 buffer: vec!["a"], 3704 set: "a".to_owned(), 3705 skipped_sets_counter: 0, 3706 skip_empty_set: true, 3707 } 3708 ); 3709 assert_eq!(power_set.next_set(), None); 3710 assert_eq!( 3711 power_set, 3712 PowerSet { 3713 feats: feats.0.as_slice(), 3714 has_remaining: false, 3715 check_overlap: false, 3716 idx: 0, 3717 buffer: vec!["a"], 3718 set: "a".to_owned(), 3719 skipped_sets_counter: 0, 3720 skip_empty_set: true, 3721 } 3722 ); 3723 power_set.reset(); 3724 assert_eq!( 3725 power_set, 3726 PowerSet { 3727 feats: feats.0.as_slice(), 3728 has_remaining: true, 3729 check_overlap: false, 3730 idx: 1, 3731 buffer: vec!["a"], 3732 set: "a".to_owned(), 3733 skipped_sets_counter: 0, 3734 skip_empty_set: true, 3735 } 3736 ); 3737 assert_eq!(power_set.next_set(), Some("a")); 3738 assert_eq!(power_set.next_set(), None); 3739 assert_eq!( 3740 power_set, 3741 PowerSet { 3742 feats: feats.0.as_slice(), 3743 has_remaining: false, 3744 check_overlap: false, 3745 idx: 0, 3746 buffer: vec!["a"], 3747 set: "a".to_owned(), 3748 skipped_sets_counter: 0, 3749 skip_empty_set: true, 3750 } 3751 ); 3752 assert_eq!(power_set.len(), NonZeroUsizePlus1::new(1)); 3753 } 3754 }