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