ci-cargo

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

manifest.rs (88731B)


      1 #![expect(clippy::std_instead_of_core, reason = "false positive")]
      2 use super::{
      3     args::NonZeroUsizePlus1,
      4     cargo::{Toolchain, ToolchainErr},
      5 };
      6 use alloc::borrow::Cow;
      7 use core::cmp::Ordering;
      8 use std::{
      9     fs::{File, TryLockError},
     10     io::{Error, ErrorKind, Read as _, StderrLock, Write as _},
     11     path::{Path, PathBuf},
     12 };
     13 use toml::{
     14     Spanned,
     15     de::{DeArray, DeValue, Error as TomlErr},
     16     map::Map,
     17 };
     18 /// Unit tests.
     19 #[cfg(test)]
     20 mod tests;
     21 /// `"workspace"`.
     22 const WORKSPACE: &str = "workspace";
     23 /// `"package"`.
     24 const PACKAGE: &str = "package";
     25 /// `"rust-version"`.
     26 const RUST_VERSION: &str = "rust-version";
     27 /// Error returned from extracting `"workspace"`.
     28 #[cfg_attr(test, derive(Debug, PartialEq))]
     29 #[derive(Clone, Copy)]
     30 pub(crate) enum WorkspaceErr {
     31     /// Variant returned when there is no `"workspace"` key.
     32     Missing,
     33     /// Variant returned when `"workspace"` is not a table.
     34     InvalidType,
     35     /// Variant returned when `workspace.package` does not exist.
     36     MissingPackage,
     37     /// Variant returned when `workspace.package` is not a table.
     38     InvalidPackageType,
     39     /// Variant returned when `workspace.package.rust-version` does not exist.
     40     MissingPackageMsrv,
     41     /// Variant returned when `workspace.package.rust-version` is not a string.
     42     InvalidPackageMsrvType,
     43     /// Variant returned when `workspace.package.rust-version` is not a valid MSRV.
     44     Msrv,
     45 }
     46 impl WorkspaceErr {
     47     /// Writes `self` to `stderr`.
     48     fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> {
     49         match self {
     50             Self::Missing => writeln!(
     51                 stderr,
     52                 "'{WORKSPACE}' does not exist in {}.",
     53                 file.display()
     54             ),
     55             Self::InvalidType => writeln!(
     56                 stderr,
     57                 "'{WORKSPACE}' exists but is not a table in {}.",
     58                 file.display()
     59             ),
     60             Self::MissingPackage => writeln!(
     61                 stderr,
     62                 "'{WORKSPACE}.{PACKAGE}' does not exist in {}.",
     63                 file.display()
     64             ),
     65             Self::InvalidPackageType => writeln!(
     66                 stderr,
     67                 "'{WORKSPACE}.{PACKAGE}' exists but is not a table in {}.",
     68                 file.display()
     69             ),
     70             Self::MissingPackageMsrv => writeln!(
     71                 stderr,
     72                 "'{WORKSPACE}.{PACKAGE}.{RUST_VERSION}' does not exist in {}.",
     73                 file.display()
     74             ),
     75             Self::InvalidPackageMsrvType => writeln!(
     76                 stderr,
     77                 "'{WORKSPACE}.{PACKAGE}.{RUST_VERSION}' exists but is not a string in {}.",
     78                 file.display()
     79             ),
     80             Self::Msrv => writeln!(
     81                 stderr,
     82                 "'{WORKSPACE}.{PACKAGE}.{RUST_VERSION}' exists but is not a valid MSRV in {}.",
     83                 file.display()
     84             ),
     85         }
     86     }
     87 }
     88 /// `"name"`.
     89 const NAME: &str = "name";
     90 /// Error returned from extracting `"package"`.
     91 #[cfg_attr(test, derive(Debug))]
     92 pub(crate) enum PackageErr {
     93     /// Variant returned when there is no `"package"` key.
     94     Missing,
     95     /// Variant returned when `"package"` is not a table.
     96     InvalidType,
     97     /// Variant returned when `packagen.name` does not exist.
     98     MissingName,
     99     /// Variant returned when `package.name` is not a string.
    100     InvalidNameType,
    101     /// Variant returned when `package.rust-version` is not a string nor table.
    102     InvalidMsrvType,
    103     /// Variant returned when `package.rust-version` is not a valid MSRV.
    104     Msrv,
    105     /// Variant returned when `package.rust-version` is table that doesn't contain the `"workspace"` key.
    106     MsrvWorkspaceMissing,
    107     /// Variant returned when `package.rust-version.workspace` is not a Boolean with value `true`.
    108     MsrvWorkspaceVal,
    109     /// Variant returned when `package.workspace` is not a string.
    110     InvalidWorkspaceType,
    111     /// Variant returned when searching for the workspace file errors.
    112     WorkspaceIo(Error),
    113     /// Variant when locking the workspace file errors.
    114     WorkspaceLock(TryLockError),
    115     /// Variant when the length of the workspace file does not match the length reported from the file system.
    116     WorkspaceLenMismatch,
    117     /// Variant returned when there is no workspace `Cargo.toml`.
    118     ///
    119     /// This is only returned if the package's MSRV is inherited from the workspace, there is no
    120     /// `workspace` key in the package's `Cargo.toml`, and there is no `workspace` key in the table
    121     /// `package` (i.e., this is only returned when we must search for it by crawling up the directory).
    122     WorkspaceDoesNotExist,
    123     /// Variant returned when the file located at `package.workspace` could not be read.
    124     ///
    125     /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched
    126     /// for the workspace and found a `Cargo.toml`.
    127     WorkspaceRead(Error, PathBuf),
    128     /// Variant returned when the file located at `package.workspace` could not be locked.
    129     ///
    130     /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched
    131     /// for the workspace and found a `Cargo.toml`.
    132     WorkspaceReadLock(TryLockError, PathBuf),
    133     /// Variant returned when the length of the file located at `package.workspace` does not match the length
    134     /// reported by the file system.
    135     ///
    136     /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched
    137     /// for the workspace and found a `Cargo.toml`.
    138     WorkspaceReadLenMismatch(PathBuf),
    139     /// Variant returned when the file located at `package.workspace` is not valid TOML.
    140     ///
    141     /// This is only returned if the table `package` had a key `workspace` that was a string, or we searched
    142     /// for the workspace and found a `Cargo.toml`.
    143     WorkspaceToml(TomlErr, PathBuf),
    144     /// Variant returned when `package.rust-version` defers to the workspace MSRV, but there was an error
    145     /// extracting the workspace MSRV from the file located at the contained `PathBuf`.
    146     Workspace(WorkspaceErr, PathBuf),
    147 }
    148 impl PackageErr {
    149     /// Writes `self` to `stderr`.
    150     #[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
    151     fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> {
    152         match self {
    153             Self::Missing => writeln!(stderr, "'{PACKAGE}' does not exist in {}.", file.display()),
    154             Self::InvalidType => writeln!(
    155                 stderr,
    156                 "'{PACKAGE}' exists but is not a table in {}.",
    157                 file.display()
    158             ),
    159             Self::MissingName => writeln!(stderr, "'{PACKAGE}.{NAME}' does not exist in {}.", file.display()),
    160             Self::InvalidNameType => writeln!(
    161                 stderr,
    162                 "'{PACKAGE}.{NAME}' exists but is not a string in {}.",
    163                 file.display()
    164             ),
    165             Self::InvalidMsrvType => writeln!(
    166                 stderr,
    167                 "'{PACKAGE}.{RUST_VERSION}' exists but is not a string nor table in {}.",
    168                 file.display()
    169             ),
    170             Self::Msrv => writeln!(
    171                 stderr,
    172                 "'{PACKAGE}.{RUST_VERSION}' is a string but is not a valid MSRV in {}.",
    173                 file.display()
    174             ),
    175             Self::MsrvWorkspaceMissing => writeln!(
    176                 stderr,
    177                 "'{PACKAGE}.{RUST_VERSION}' is a table but does not contain the key '{WORKSPACE}' in {}.",
    178                 file.display()
    179             ),
    180             Self::MsrvWorkspaceVal => writeln!(
    181                 stderr,
    182                 "'{PACKAGE}.{RUST_VERSION}.{WORKSPACE}' exists but is not a Boolean or is not true in {}.",
    183                 file.display()
    184             ),
    185             Self::WorkspaceIo(e) => writeln!(
    186                 stderr,
    187                 "There was an error looking for the workspace Cargo.toml in {} and its ancestor directories: {e}.",
    188                 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(),
    189             ),
    190             Self::WorkspaceLock(e) => writeln!(
    191                 stderr,
    192                 "There was an error locking the workspace Cargo.toml in {} and its ancestor directories: {e}.",
    193                 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(),
    194             ),
    195             Self::WorkspaceLenMismatch=> writeln!(
    196                 stderr,
    197                 "The length of the workspace Cargo.toml in {} does not match the length reported by the file systemn.",
    198                 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(),
    199             ),
    200             Self::WorkspaceDoesNotExist => writeln!(
    201                 stderr,
    202                 "There is no workspace Cargo.toml in {} nor its ancestor directories.",
    203                 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(),
    204             ),
    205             Self::InvalidWorkspaceType => writeln!(
    206                 stderr,
    207                 "'{PACKAGE}.{WORKSPACE}' exists but is not a string in {}.",
    208                 file.display()
    209             ),
    210             Self::WorkspaceRead(e, p) => {
    211                 writeln!(stderr, "There was an issue reading the workspace file {}: {e}.", p.display())
    212             }
    213             Self::WorkspaceReadLock(e, p) => {
    214                 writeln!(stderr, "There was an issue locking the workspace file {}: {e}.", p.display())
    215             }
    216             Self::WorkspaceReadLenMismatch(p) => {
    217                 writeln!(stderr, "The length of the workspace file {} does not match the length reported by the file system.", p.display())
    218             }
    219             Self::WorkspaceToml(e, p) => write!(
    220                 stderr,
    221                 "Error parsing workspace file {} as TOML: {e}.",
    222                 p.display()
    223             ),
    224             Self::Workspace(e, p) => e.write(stderr, &p),
    225         }
    226     }
    227 }
    228 /// `"features"`.
    229 const FEATURES: &str = "features";
    230 /// Error returned from extracting feature dependencies.
    231 #[cfg_attr(test, derive(Debug, PartialEq))]
    232 pub(crate) enum FeatureDependenciesErr {
    233     /// Variant returned when a feature is not an array.
    234     ///
    235     /// The contained `String` is the name of the feature.
    236     InvalidFeatureType(String),
    237     /// Variant returned when a feature dependency is not a string.
    238     ///
    239     /// The contained `String` is the name of the feature.
    240     InvalidDependencyType(String),
    241     /// Variant returned when a feature dependency is not a feature nor dependency.
    242     ///
    243     /// The first contained `String` is the name of the feature, and the second `String` is the name of the invalid
    244     /// feature dependency.
    245     ///
    246     /// Note this is only possible when `allow_implied_features` is `false` when passed to
    247     /// [`Features::validate_dependencies`].
    248     InvalidDependency(String, String),
    249     /// Variant returned when a feature is cyclic.
    250     ///
    251     /// The contained `String` is the name of the feature.
    252     CyclicFeature(String),
    253     /// Variant returned when a feature dependency is redundant.
    254     ///
    255     /// The first contained `String` is the name of the feature, and the second `String` is the name of the
    256     /// redundant feature dependency.
    257     RedundantDependency(String, String),
    258 }
    259 impl FeatureDependenciesErr {
    260     /// Writes `self` to `stderr`.
    261     fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> {
    262         match self {
    263             Self::InvalidFeatureType(name) => {
    264                 writeln!(
    265                     stderr,
    266                     "'{FEATURES}.{name}' is not an array in {}.",
    267                     file.display()
    268                 )
    269             }
    270             Self::InvalidDependencyType(name) => writeln!(
    271                 stderr,
    272                 "'{FEATURES}.{name}' contains a value that is not a string in {}.",
    273                 file.display()
    274             ),
    275             Self::InvalidDependency(name, dep_name) => writeln!(
    276                 stderr,
    277                 "'{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.",
    278                 file.display()
    279             ),
    280             Self::CyclicFeature(name) => writeln!(
    281                 stderr,
    282                 "'{FEATURES}.{name}' is a cyclic feature in {}.",
    283                 file.display()
    284             ),
    285             Self::RedundantDependency(name, dep_name) => writeln!(
    286                 stderr,
    287                 "'{FEATURES}.{name}' contains the redundant dependency '{dep_name}' in {}.",
    288                 file.display()
    289             ),
    290         }
    291     }
    292 }
    293 /// Error returned from extracting `"features"`.
    294 #[cfg_attr(test, derive(Debug, PartialEq))]
    295 pub(crate) enum FeaturesErr {
    296     /// Variant returned when `features` is not a table in Cargo.toml.
    297     InvalidType,
    298     /// Variant returned when `features` contains a feature with an invalid name.
    299     ///
    300     /// The contained `String` is the name of the feature.
    301     InvalidName(String),
    302     /// Variant returned when there is an issue with a feature's dependencies.
    303     FeatureDependencies(FeatureDependenciesErr),
    304 }
    305 impl FeaturesErr {
    306     /// Writes `self` to `stderr`.
    307     fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> {
    308         match self {
    309             Self::InvalidType => {
    310                 writeln!(
    311                     stderr,
    312                     "'{FEATURES}' exists but is not a table in {}.",
    313                     file.display()
    314                 )
    315             }
    316             Self::InvalidName(name) => {
    317                 writeln!(
    318                     stderr,
    319                     "'{FEATURES}.{name}' is not a valid feature name in {}.",
    320                     file.display()
    321                 )
    322             }
    323             Self::FeatureDependencies(e) => e.write(stderr, file),
    324         }
    325     }
    326 }
    327 /// Error returned from extracting dependencies.
    328 #[cfg_attr(test, derive(Debug, PartialEq))]
    329 pub(crate) enum DependenciesErr {
    330     /// Variant returned when the dependencies is not a table.
    331     ///
    332     /// The contained `str` is the name of the dependencies (e.g., `"dependencies"`).
    333     Type(&'static str),
    334     /// Variant returned when a dependency has an invalid name.
    335     ///
    336     /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the
    337     /// name of the dependency.
    338     Name(&'static str, String),
    339     /// Variant returned when a dependency is not a string or table.
    340     ///
    341     /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the
    342     /// name of the dependency.
    343     DependencyType(&'static str, String),
    344     /// Variant returned when a dependency contains an `"optional"` key whose value is not a Boolean.
    345     ///
    346     /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the
    347     /// name of the dependency.
    348     OptionalType(&'static str, String),
    349     /// Variant returned when an optional dependency would cause an implied feature to be created.
    350     ///
    351     /// Note this is only possible when `allow_implied_features` is `false` when passed to
    352     /// [`Features::add_optional_dependencies`].
    353     ///
    354     /// The contained `str` is the name of dependencies (e.g., `"dependencies"`), and the `String` is the
    355     /// name of the dependency.
    356     ImpliedFeature(&'static str, String),
    357 }
    358 /// Error returned from extracting dependencies to add implied features.
    359 #[cfg_attr(test, derive(Debug, PartialEq))]
    360 pub(crate) enum ImpliedFeaturesErr {
    361     /// Variant returned from extracting dependencies.
    362     Dependencies(DependenciesErr),
    363     /// Variant returned when `target` is not a table in Cargo.toml.
    364     TargetType,
    365     /// Variant returned when `target` contains a key whose value is not a table.
    366     ///
    367     /// The contained `String` is the name of the key.
    368     TargetPlatformType(String),
    369     /// Variant returned when a target platform contains an issue with dependencies.
    370     ///
    371     /// The contained `String` is the name of the target platform.
    372     TagetPlatformDependencies(String, DependenciesErr),
    373     /// Variant returned when a feature dependency is not a feature nor dependency.
    374     ///
    375     /// The first contained `String` is the name of the feature, and the second `String` is the name of the invalid
    376     /// feature dependency.
    377     ///
    378     /// Note this is only possible when `allow_implied_features` is `true` when passed to
    379     /// [`Features::validate_dependencies`] since when `false` we verify the the dependency
    380     /// is defined as a feature.
    381     InvalidDependency(String, String),
    382 }
    383 /// `"optional"`.
    384 const OPTIONAL: &str = "optional";
    385 /// `"target"`.
    386 const TARGET: &str = "target";
    387 impl ImpliedFeaturesErr {
    388     /// Writes `self` to `stderr`.
    389     fn write(self, mut stderr: StderrLock<'_>, file: &Path) -> Result<(), Error> {
    390         match self {
    391             Self::Dependencies(e) => match e {
    392                 DependenciesErr::Type(name) => {
    393                     writeln!(
    394                         stderr,
    395                         "'{name}' exists but is not a table in {}.",
    396                         file.display()
    397                     )
    398                 }
    399                 DependenciesErr::Name(name, dep_name) => {
    400                     writeln!(
    401                         stderr,
    402                         "'{name}.{dep_name}' is not a valid dependency name in {}.",
    403                         file.display()
    404                     )
    405                 }
    406                 DependenciesErr::DependencyType(name, dep_name) => {
    407                     writeln!(
    408                         stderr,
    409                         "'{name}.{dep_name}' exists but is not a string nor table in {}.",
    410                         file.display()
    411                     )
    412                 }
    413                 DependenciesErr::OptionalType(name, dep_name) => {
    414                     writeln!(
    415                         stderr,
    416                         "'{name}.{dep_name}.{OPTIONAL}' exists but is not a Boolean in {}.",
    417                         file.display()
    418                     )
    419                 }
    420                 DependenciesErr::ImpliedFeature(name, dep_name) => {
    421                     writeln!(
    422                         stderr,
    423                         "'{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.",
    424                         file.display()
    425                     )
    426                 }
    427             },
    428             Self::TargetType => {
    429                 writeln!(
    430                     stderr,
    431                     "'{TARGET}' exists but is not a table in {}.",
    432                     file.display()
    433                 )
    434             }
    435             Self::TargetPlatformType(name) => {
    436                 writeln!(
    437                     stderr,
    438                     "'{TARGET}.{name}' exists but is not a table in {}.",
    439                     file.display()
    440                 )
    441             }
    442             Self::TagetPlatformDependencies(name, e) => match e {
    443                 DependenciesErr::Type(table_name) => {
    444                     writeln!(
    445                         stderr,
    446                         "'{TARGET}.{name}.{table_name}' exists but is not a table in {}.",
    447                         file.display()
    448                     )
    449                 }
    450                 DependenciesErr::Name(table_name, dep_name) => {
    451                     writeln!(
    452                         stderr,
    453                         "'{TARGET}.{name}.{table_name}.{dep_name}' is not a valid dependency name in {}.",
    454                         file.display()
    455                     )
    456                 }
    457                 DependenciesErr::DependencyType(table_name, dep_name) => writeln!(
    458                     stderr,
    459                     "'{TARGET}.{name}.{table_name}.{dep_name}' exists but is not a string nor table in {}.",
    460                     file.display()
    461                 ),
    462                 DependenciesErr::OptionalType(table_name, dep_name) => writeln!(
    463                     stderr,
    464                     "'{TARGET}.{name}.{table_name}.{dep_name}.{OPTIONAL}' exists but is not a Boolean in {}.",
    465                     file.display()
    466                 ),
    467                 DependenciesErr::ImpliedFeature(table_name, dep_name) => {
    468                     writeln!(
    469                         stderr,
    470                         "'{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.",
    471                         file.display()
    472                     )
    473                 }
    474             },
    475             Self::InvalidDependency(name, dep_name) => writeln!(
    476                 stderr,
    477                 "'{FEATURES}.{name}' contains '{dep_name}' which is neither a feature nor dependency in {}.",
    478                 file.display()
    479             ),
    480         }
    481     }
    482 }
    483 /// Error returned from parsing Cargo.toml.
    484 #[cfg_attr(test, derive(Debug, PartialEq))]
    485 pub(crate) enum ManifestErr {
    486     /// Variant returned when Cargo.toml is not valid TOML.
    487     Toml(TomlErr, PathBuf),
    488     /// Variant returned when extracting `package`.
    489     Package(PackageErr, PathBuf),
    490     /// Variant returned when extracting `features`.
    491     Features(FeaturesErr, PathBuf),
    492     /// Variant returned when extracting dependencies in order to add implied features.
    493     ImpliedFeatures(ImpliedFeaturesErr, PathBuf),
    494     /// Variant returned when ignoring a feature that does not exist.
    495     UndefinedIgnoreFeature(String, PathBuf),
    496 }
    497 impl ManifestErr {
    498     /// Writes `self` to `stderr`.
    499     pub(crate) fn write(self, mut stderr: StderrLock<'_>) -> Result<(), Error> {
    500         match self {
    501             Self::Toml(e, file) => write!(
    502                 stderr,
    503                 "Error parsing package file {} as TOML: {e}.",
    504                 file.display()
    505             ),
    506             Self::Package(e, file) => e.write(stderr, &file),
    507             Self::Features(e, file) => e.write(stderr, &file),
    508             Self::ImpliedFeatures(e, file) => e.write(stderr, &file),
    509             Self::UndefinedIgnoreFeature(feature, file) => writeln!(
    510                 stderr,
    511                 "The feature '{feature}' was requested to be ignored, but it is not a feature in the package file {}.",
    512                 file.display()
    513             ),
    514         }
    515     }
    516 }
    517 /// Error when there are too many features to create the power set.
    518 #[cfg_attr(test, derive(Debug, PartialEq))]
    519 pub(crate) struct TooManyFeaturesErr;
    520 /// Parses `val` as a `u64` in decimal notation without leading 0s.
    521 ///
    522 /// # Errors
    523 ///
    524 /// Errors iff `val` is not a valid `u64` in decimal notation without leading 0s.
    525 pub(crate) fn parse_int(val: &str) -> Result<u64, ()> {
    526     val.as_bytes().first().ok_or(()).and_then(|fst| {
    527         if *fst == b'0' {
    528             if val.len() == 1 { Ok(0) } else { Err(()) }
    529         } else {
    530             val.parse().map_err(|_e| ())
    531         }
    532     })
    533 }
    534 /// MSRV in Cargo.toml.
    535 #[cfg_attr(test, derive(Debug, PartialEq))]
    536 pub(crate) struct Msrv {
    537     /// Major version.
    538     major: u64,
    539     /// Minor version.
    540     minor: Option<u64>,
    541     /// Patch version.
    542     patch: Option<u64>,
    543 }
    544 impl Msrv {
    545     /// Converts `msrv` into `Self` based on a valid MSRV string.
    546     #[expect(unsafe_code, reason = "comments justify their correctness")]
    547     fn extract_msrv(msrv: &str) -> Result<Self, ()> {
    548         let mut iter = msrv.as_bytes().split(|b| *b == b'.');
    549         iter.next().ok_or(()).and_then(|fst| {
    550             // SAFETY:
    551             // The original input is a `str` and we split on `b'.'` which is a single-byte
    552             // UTF-8 code unit; thus we don't have to worry about splitting a multi-byte
    553             // UTF-8 code unit.
    554             let major_utf8 = unsafe { str::from_utf8_unchecked(fst) };
    555             parse_int(major_utf8).and_then(|major| {
    556                 iter.next().map_or_else(
    557                     || {
    558                         Ok(Self {
    559                             major,
    560                             minor: None,
    561                             patch: None,
    562                         })
    563                     },
    564                     |snd| {
    565                         // SAFETY:
    566                         // The original input is a `str` and we split on `b'.'` which is
    567                         // a single-byte UTF-8 code unit; thus we don't have to worry
    568                         // about splitting a multi-byte UTF-8 code unit.
    569                         let minor_utf8 = unsafe { str::from_utf8_unchecked(snd) };
    570                         parse_int(minor_utf8).and_then(|minor_val| {
    571                             iter.next().map_or_else(
    572                                 || {
    573                                     Ok(Self {
    574                                         major,
    575                                         minor: Some(minor_val),
    576                                         patch: None,
    577                                     })
    578                                 },
    579                                 |lst| {
    580                                     // SAFETY:
    581                                     // The original input is a `str` and we split on
    582                                     // `b'.'` which is a single-byte UTF-8 code
    583                                     // unit; thus we don't have to worry about
    584                                     // splitting a multi-byte UTF-8 code unit.
    585                                     let patch_utf8 = unsafe { str::from_utf8_unchecked(lst) };
    586                                     parse_int(patch_utf8).and_then(|patch_val| {
    587                                         iter.next().map_or_else(
    588                                             || {
    589                                                 Ok(Self {
    590                                                     major,
    591                                                     minor: Some(minor_val),
    592                                                     patch: Some(patch_val),
    593                                                 })
    594                                             },
    595                                             |_| Err(()),
    596                                         )
    597                                     })
    598                                 },
    599                             )
    600                         })
    601                     },
    602                 )
    603             })
    604         })
    605     }
    606     /// Reads `workspace` from `toml` extracting the MSRV.
    607     fn extract_workspace(
    608         toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
    609     ) -> Result<Self, WorkspaceErr> {
    610         toml.get(WORKSPACE)
    611             .ok_or(WorkspaceErr::Missing)
    612             .and_then(|work_span| {
    613                 if let DeValue::Table(ref workspace) = *work_span.get_ref() {
    614                     workspace
    615                         .get(PACKAGE)
    616                         .ok_or(WorkspaceErr::MissingPackage)
    617                         .and_then(|pack_span| {
    618                             if let DeValue::Table(ref package) = *pack_span.get_ref() {
    619                                 package
    620                                     .get(RUST_VERSION)
    621                                     .ok_or(WorkspaceErr::MissingPackageMsrv)
    622                                     .and_then(|msrv_span| {
    623                                         if let DeValue::String(ref msrv) = *msrv_span.get_ref() {
    624                                             Self::extract_msrv(msrv)
    625                                                 .map_err(|()| WorkspaceErr::Msrv)
    626                                         } else {
    627                                             Err(WorkspaceErr::InvalidPackageMsrvType)
    628                                         }
    629                                     })
    630                             } else {
    631                                 Err(WorkspaceErr::InvalidPackageType)
    632                             }
    633                         })
    634                 } else {
    635                     Err(WorkspaceErr::InvalidType)
    636                 }
    637             })
    638     }
    639     /// Recursively looks for `Cargo.toml` in `cur_dir` and ancestor directories until one is found
    640     /// that contains a key named [`WORKSPACE`]. Once found, it's MSRV will be parsed and returned.
    641     ///
    642     /// We make this recursive in case the (impossible?) path traversal becomes cyclic; in which
    643     /// we want a stack overflow to occur.
    644     ///
    645     /// Note if any error occurs not related to a not found file error, then this will error.
    646     #[expect(
    647         clippy::verbose_file_reads,
    648         reason = "false positive since we want to lock the file"
    649     )]
    650     fn get_workspace_toml(mut cur_dir: PathBuf) -> Result<Self, PackageErr> {
    651         cur_dir.push(super::cargo_toml());
    652         match File::options()
    653             .read(true)
    654             .open(&cur_dir)
    655         {
    656             Ok(mut file) => {
    657                 file.try_lock_shared()
    658                     .map_err(PackageErr::WorkspaceLock)
    659                     .and_then(|()| {
    660                         file.metadata()
    661                             .map_err(PackageErr::WorkspaceIo)
    662                             .and_then(|meta| {
    663                                 let meta_len = usize::try_from(meta.len()).unwrap_or(usize::MAX);
    664                                 let mut data_utf8 = Vec::with_capacity(meta_len);
    665                                 file.read_to_end(&mut data_utf8)
    666                                     .map_err(PackageErr::WorkspaceIo)
    667                                     .and_then(|len| {
    668                                         drop(file);
    669                                         if meta_len == len {
    670                                             String::from_utf8(data_utf8)
    671                                                 .map_err(|e| PackageErr::WorkspaceIo(Error::other(e)))
    672                                                 .and_then(|data| {
    673                                                     Map::parse(&data)
    674                                                         .map_err(|e| PackageErr::WorkspaceToml(e, cur_dir.clone()))
    675                                                         .and_then(|toml| {
    676                                                             let t = toml.into_inner();
    677                                                             if t.contains_key(WORKSPACE) {
    678                                                                 Self::extract_workspace(&t)
    679                                                                     .map_err(|e| PackageErr::Workspace(e, cur_dir))
    680                                                             } else {
    681                                                                 _ = cur_dir.pop();
    682                                                                 if cur_dir.pop() {
    683                                                                     Self::get_workspace_toml(cur_dir)
    684                                                                 } else {
    685                                                                     Err(PackageErr::WorkspaceDoesNotExist)
    686                                                                 }
    687                                                             }
    688                                                         })
    689                                                 })
    690                                         } else {
    691                                             Err(PackageErr::WorkspaceLenMismatch)
    692                                         }
    693                                     })
    694                             })
    695                     })
    696             }
    697             Err(e) => {
    698                 if matches!(e.kind(), ErrorKind::NotFound) {
    699                     _ = cur_dir.pop();
    700                     if cur_dir.pop() {
    701                         Self::get_workspace_toml(cur_dir)
    702                     } else {
    703                         Err(PackageErr::WorkspaceIo(e))
    704                     }
    705                 } else {
    706                     Err(PackageErr::WorkspaceIo(e))
    707                 }
    708             }
    709         }
    710     }
    711     /// Extracts `"rust-version"` from `"package"` or `toml` in the case it's defined in a workspace.
    712     #[expect(
    713         clippy::panic_in_result_fn,
    714         reason = "want to crash when there is a bug"
    715     )]
    716     #[expect(
    717         clippy::verbose_file_reads,
    718         reason = "false positive since we want to lock the file"
    719     )]
    720     fn extract_from_toml(
    721         toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
    722         package: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
    723         cargo_toml: &Path,
    724     ) -> Result<Option<Self>, PackageErr> {
    725         package.get(RUST_VERSION).map_or(Ok(None), |msrv_span| {
    726             match *msrv_span.get_ref() {
    727                 DeValue::String(ref msrv) => Self::extract_msrv(msrv)
    728                     .map_err(|()| PackageErr::Msrv)
    729                     .map(Some),
    730                 DeValue::Table(ref msrv) => msrv
    731                     .get(WORKSPACE)
    732                     .ok_or(PackageErr::MsrvWorkspaceMissing)
    733                     .and_then(|work| {
    734                         if matches!(*work.get_ref(), DeValue::Boolean(b) if b) {
    735                             package.get(WORKSPACE).map_or_else(
    736                                 || if toml.contains_key(WORKSPACE) {
    737                                     Self::extract_workspace(toml).map_err(|e| PackageErr::Workspace(e, cargo_toml.to_path_buf())).map(Some)
    738                                 } else {
    739                                     let mut search_path = cargo_toml.to_path_buf();
    740                                     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.");
    741                                     if search_path.pop() {
    742                                         Self::get_workspace_toml(search_path).map(Some)
    743                                     } else {
    744                                         Err(PackageErr::WorkspaceDoesNotExist)
    745                                     }
    746                                 },
    747                                 |path_span| {
    748                                     if let DeValue::String(ref workspace_path) = *path_span.get_ref() {
    749                                         let mut path = cargo_toml.to_path_buf();
    750                                         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.");
    751                                         path.push(workspace_path.as_ref());
    752                                         path.push(super::cargo_toml());
    753                                         File::options().read(true).open(&path).map_err(|e| PackageErr::WorkspaceRead(e, path.clone())).and_then(|mut workspace_file| {
    754                                             workspace_file.try_lock_shared().map_err(|e| PackageErr::WorkspaceReadLock(e, path.clone())).and_then(|()| {
    755                                                 workspace_file.metadata().map_err(|e| PackageErr::WorkspaceRead(e, path.clone())).and_then(|meta| {
    756                                                     let meta_len = usize::try_from(meta.len()).unwrap_or(usize::MAX);
    757                                                     let mut workspace_utf8 = Vec::with_capacity(meta_len);
    758                                                     workspace_file.read_to_end(&mut workspace_utf8).map_err(|e| PackageErr::WorkspaceRead(e, path.clone())).and_then(|len| {
    759                                                         drop(workspace_file);
    760                                                         if meta_len == len {
    761                                                             String::from_utf8(workspace_utf8).map_err(|e| PackageErr::WorkspaceRead(Error::other(e), path.clone())).and_then(|workspace_data| {
    762                                                                 Map::parse(&workspace_data).map_err(|e| PackageErr::WorkspaceToml(e, path.clone())).and_then(|workspace_toml| Self::extract_workspace(workspace_toml.get_ref()).map_err(|e| PackageErr::Workspace(e, path)).map(Some))
    763                                                             })
    764                                                         } else {
    765                                                             Err(PackageErr::WorkspaceReadLenMismatch(path))
    766                                                         }
    767                                                     })
    768                                                 })
    769                                             })
    770                                         })
    771                                     } else {
    772                                         Err(PackageErr::InvalidWorkspaceType)
    773                                     }
    774                                 },
    775                             )
    776                         } else {
    777                             Err(PackageErr::MsrvWorkspaceVal)
    778                         }
    779                     }),
    780                 DeValue::Integer(_)
    781                 | DeValue::Float(_)
    782                 | DeValue::Boolean(_)
    783                 | DeValue::Datetime(_)
    784                 | DeValue::Array(_) => Err(PackageErr::InvalidMsrvType),
    785             }
    786         })
    787     }
    788     /// Returns `Some` containing the MSRV with `'+'` prepended iff `stable` is semantically greater than `self`
    789     /// or the default toolchan is semantically not equivalent to `self`; otherwise returns `None`.
    790     ///
    791     /// When `stable` is semantically less than the MSRV, an error is returned.
    792     pub(crate) fn compare_to_other(
    793         &self,
    794         default: bool,
    795         rustup_home: Option<&Path>,
    796         cargo_path: &Path,
    797         cargo_home: Option<&Path>,
    798     ) -> Result<Option<String>, Box<ToolchainErr>> {
    799         if default {
    800             Toolchain::Default(false)
    801         } else {
    802             Toolchain::Stable
    803         }
    804         .get_version(rustup_home, cargo_path, cargo_home)
    805         .and_then(|stable_dflt_version| {
    806             match self.major.cmp(&stable_dflt_version.major) {
    807                 Ordering::Less => Ok(true),
    808                 Ordering::Equal => self.minor.map_or_else(
    809                     || Ok(false),
    810                     |min| match min.cmp(&stable_dflt_version.minor) {
    811                         Ordering::Less => Ok(true),
    812                         Ordering::Equal => self.patch.map_or_else(
    813                             || Ok(false),
    814                             |pat| match pat.cmp(&stable_dflt_version.patch) {
    815                                 Ordering::Less => Ok(true),
    816                                 Ordering::Equal => Ok(false),
    817                                 Ordering::Greater => {
    818                                     if default {
    819                                         Ok(true)
    820                                     } else {
    821                                         Err(Box::new(ToolchainErr::MsrvTooHigh))
    822                                     }
    823                                 }
    824                             },
    825                         ),
    826                         Ordering::Greater => {
    827                             if default {
    828                                 Ok(true)
    829                             } else {
    830                                 Err(Box::new(ToolchainErr::MsrvTooHigh))
    831                             }
    832                         }
    833                     },
    834                 ),
    835                 Ordering::Greater => {
    836                     if default {
    837                         Ok(true)
    838                     } else {
    839                         Err(Box::new(ToolchainErr::MsrvTooHigh))
    840                     }
    841                 }
    842             }
    843             .and_then(|get_msrv| {
    844                 if get_msrv {
    845                     Toolchain::Msrv(&self.minor.map_or_else(
    846                         || format!("+{}", self.major),
    847                         |min| {
    848                             self.patch.map_or_else(
    849                                 || format!("+{}.{min}", self.major),
    850                                 |pat| format!("+{}.{min}.{pat}", self.major),
    851                             )
    852                         },
    853                     ))
    854                     .get_version(rustup_home, cargo_path, cargo_home)
    855                     .and_then(|msrv_version| {
    856                         if msrv_version.major == self.major
    857                             && self.minor.is_none_or(|minor| {
    858                                 msrv_version.minor == minor
    859                                     && self.patch.is_none_or(|patch| msrv_version.patch == patch)
    860                             })
    861                         {
    862                             Ok(Some(format!(
    863                                 "+{}.{}.{}",
    864                                 msrv_version.major, msrv_version.minor, msrv_version.patch
    865                             )))
    866                         } else {
    867                             Err(Box::new(ToolchainErr::MsrvNotCompatibleWithInstalledMsrv(
    868                                 msrv_version,
    869                             )))
    870                         }
    871                     })
    872                 } else {
    873                     Ok(None)
    874                 }
    875             })
    876         })
    877     }
    878 }
    879 /// `package` info.
    880 #[cfg_attr(test, derive(Debug, PartialEq))]
    881 pub(crate) struct Package {
    882     /// `rust-version`.
    883     msrv: Option<Msrv>,
    884     /// `name`.
    885     name: String,
    886 }
    887 impl Package {
    888     /// MSRV.
    889     pub(crate) const fn msrv(&self) -> Option<&Msrv> {
    890         self.msrv.as_ref()
    891     }
    892     /// Name.
    893     pub(crate) const fn name(&self) -> &str {
    894         self.name.as_str()
    895     }
    896     /// Extracts `"package"` from `toml`.
    897     fn extract_from_toml(
    898         toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
    899         cargo_toml: &Path,
    900     ) -> Result<Self, PackageErr> {
    901         toml.get(PACKAGE)
    902             .ok_or(PackageErr::Missing)
    903             .and_then(|pack_span| {
    904                 if let DeValue::Table(ref package) = *pack_span.get_ref() {
    905                     package
    906                         .get(NAME)
    907                         .ok_or(PackageErr::MissingName)
    908                         .and_then(|name_span| {
    909                             if let DeValue::String(ref name) = *name_span.get_ref() {
    910                                 Msrv::extract_from_toml(toml, package, cargo_toml).map(|msrv| {
    911                                     Self {
    912                                         msrv,
    913                                         name: name.clone().into_owned(),
    914                                     }
    915                                 })
    916                             } else {
    917                                 Err(PackageErr::InvalidNameType)
    918                             }
    919                         })
    920                 } else {
    921                     Err(PackageErr::InvalidType)
    922                 }
    923             })
    924     }
    925 }
    926 /// Returns `true` iff `nodes` is pairwise disconnected.
    927 #[expect(
    928     clippy::arithmetic_side_effects,
    929     clippy::indexing_slicing,
    930     reason = "comment justifies correctness"
    931 )]
    932 fn pairwise_disconnected(nodes: &[&str], features: &[(String, Vec<String>)]) -> bool {
    933     /// `panic`s with a static message about the existence of a bug in [`Manifest::deserialize`].
    934     #[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
    935     fn impossible<T>() -> T {
    936         unreachable!(
    937             "there is a bug in manifest::Manifest::deserialize where feature dependencies are not only features."
    938         )
    939     }
    940     /// Returns `true` iff `feature_deps` directly contains `feature` or indirectly in the `Vec<String>`
    941     /// associated with it.
    942     fn contains(
    943         feature_deps: &[String],
    944         feature: &str,
    945         features: &[(String, Vec<String>)],
    946     ) -> bool {
    947         feature_deps.iter().any(|feat| {
    948             feat == feature
    949                 || contains(
    950                     &features
    951                         .iter()
    952                         .find(|val| val.0 == *feat)
    953                         .unwrap_or_else(impossible)
    954                         .1,
    955                     feature,
    956                     features,
    957                 )
    958         })
    959     }
    960     !nodes.iter().enumerate().any(|(idx, feature)| {
    961         let feature_info = &features
    962             .iter()
    963             .find(|val| val.0 == **feature)
    964             .unwrap_or_else(impossible)
    965             .1;
    966         // `idx < nodes.len()`, so overflow is not possible and indexing is fine.
    967         nodes[idx + 1..].iter().any(|feat| {
    968             contains(feature_info, feat, features)
    969                 || contains(
    970                     features
    971                         .iter()
    972                         .find(|val| val.0 == **feat)
    973                         .unwrap_or_else(impossible)
    974                         .1
    975                         .as_slice(),
    976                     feature,
    977                     features,
    978                 )
    979         })
    980     })
    981 }
    982 /// Power set of [`Features`] returned from [`Features::power_set`].
    983 ///
    984 /// Note this is technically not the power set of features since semantically equivalent sets are ignored.
    985 ///
    986 /// The last set iterated will always be the empty set. If no features in [`Features`] depend on another
    987 /// feature, then this will always return the entire set of features first; otherwise the entire set will
    988 /// never be returned. The expected cardinality of a set iterated decreases; thus while it will almost always
    989 /// be possible for a set _A_ to have larger cardinality than a set _B_ even when _A_ is iterated before _B_,
    990 /// the expected value is smaller.
    991 ///
    992 /// The reason we attempt, but don't guarantee, that the first set iterated corresponds to a semantically
    993 /// equivalent set as the original set is to take advantage of the parallel compiliation that occurs. Typically
    994 /// the more features one enables, the more dependencies and functionality is added. By compiling code that
    995 /// maximizes this first, we take better advantage of how code is compiled; in contrast if we compiled fewer
    996 /// features first, subsequent compilations with more features will have to happen. We don't guarantee this
    997 /// though since it slightly complicates code; instead we iterate based on the _expected_ cardinality in
    998 /// descending order.
    999 ///
   1000 /// We don't implement `Iterator` since we want to re-use the same `String` that represents the set we are
   1001 /// returning.
   1002 #[cfg_attr(test, derive(Debug, PartialEq))]
   1003 pub(crate) struct PowerSet<'a> {
   1004     /// The set of features.
   1005     feats: &'a [(String, Vec<String>)],
   1006     /// `true` iff there are more sets to iterate.
   1007     has_remaining: bool,
   1008     /// `true` iff `feats` has redundant features; thus requiring us to check if a given set should be returned.
   1009     check_overlap: bool,
   1010     /// The current element of the power set that we are to return.
   1011     ///
   1012     /// This gets decremented as we iterate sets.
   1013     idx: usize,
   1014     /// Intermediate buffer we use to check if a set contains redundant features.
   1015     buffer: Vec<&'a str>,
   1016     /// The set we return.
   1017     ///
   1018     /// This is of the form `"<feat_1>,<feat_2>,...,<feat_n>"`.
   1019     set: String,
   1020     /// Number of sets skipped due to an equivalence with a smaller set.
   1021     skipped_sets_counter: usize,
   1022     /// `true` iff they empty set should be skipped.
   1023     ///
   1024     /// This doesn't contribute to [`Self::skipped_sets_counter`].
   1025     skip_empty_set: bool,
   1026 }
   1027 impl<'a> PowerSet<'a> {
   1028     /// Max cardinality of a set we allow to take the power set of.
   1029     // usize::MAX = 2^usize::BITS - 1 >= usize::BITS since usize::MAX >= 0;
   1030     // thus `usize::BITS as usize` is free from truncation.
   1031     #[expect(clippy::as_conversions, reason = "comment justifies correctness")]
   1032     const MAX_SET_LEN: usize = usize::BITS as usize;
   1033     /// Returns the cardinality less one of the power set of a set
   1034     /// whose cardinality is `set_len`.
   1035     ///
   1036     /// `set_len` MUST not be greater than [`Self::MAX_SET_LEN`].
   1037     #[expect(
   1038         clippy::arithmetic_side_effects,
   1039         reason = "comment justifies correctness"
   1040     )]
   1041     const fn len_minus_one(set_len: usize) -> usize {
   1042         assert!(
   1043             set_len <= Self::MAX_SET_LEN,
   1044             "manifest::PowerSet::len_minus_one must be passed a `usize` no larger than PowerSet::MAX_SET_LEN"
   1045         );
   1046         if set_len == Self::MAX_SET_LEN {
   1047             usize::MAX
   1048         } else {
   1049             // We verified that `set_len <= usize::BITS`; thus
   1050             // this won't overflow nor underflow since 2^0 = 1.
   1051             (1 << set_len) - 1
   1052         }
   1053     }
   1054     /// Returns the cardinality of `self`.
   1055     ///
   1056     /// Note this is constant and is not affected by iteration of
   1057     /// `self`. This isn't the remaining length. [`Self::skip_empty_set`]
   1058     /// contributes towards this value.
   1059     #[expect(
   1060         clippy::arithmetic_side_effects,
   1061         reason = "comment justifies correctness"
   1062     )]
   1063     pub(crate) fn len(&self) -> NonZeroUsizePlus1 {
   1064         // We don't allow construction of `PowerSet` unless the set of features
   1065         // is no more than [`Self::MAX_SET_LEN`]; thus this won't `panic`.
   1066         // Underflow won't occur either since we don't allow construction when the
   1067         // set of features is empty and when we must skip the empty set.
   1068         // `NonZeroUsizePlus1` treats `0` as `usize::MAX + 1`, so we use wrapping addition.
   1069         // This will only wrapp when `self.features.len() == Self::MAX_SET_LEN` and `!self.skip_empty_set`.
   1070         NonZeroUsizePlus1::new(
   1071             (Self::len_minus_one(self.feats.len()) - usize::from(self.skip_empty_set))
   1072                 .wrapping_add(1),
   1073         )
   1074     }
   1075     /// Contructs `Self` based on `features`.
   1076     ///
   1077     /// When iterating the sets, the empty set will be skipped iff `skip_empty_set`;
   1078     /// but this _won't_ contribute to `skipped_sets_counter`.
   1079     ///
   1080     /// Returns `None` iff `features` is empty and `skip_empty_set`.
   1081     fn new(
   1082         features: &'a Features,
   1083         skip_empty_set: bool,
   1084     ) -> Result<Option<Self>, TooManyFeaturesErr> {
   1085         let len = features.0.len();
   1086         match len {
   1087             0 if skip_empty_set => Ok(None),
   1088             ..=Self::MAX_SET_LEN => {
   1089                 let mut buffer = Vec::with_capacity(len);
   1090                 features.0.iter().fold((), |(), key| {
   1091                     buffer.push(key.0.as_ref());
   1092                 });
   1093                 let check_overlap = !pairwise_disconnected(buffer.as_slice(), &features.0);
   1094                 Ok(Some(Self {
   1095                     feats: &features.0,
   1096                     has_remaining: true,
   1097                     check_overlap,
   1098                     // We verified `len <= Self::MAX_SET_LEN`, so this won't `panic`.
   1099                     idx: Self::len_minus_one(len),
   1100                     buffer,
   1101                     // This won't overflow since `usize::MAX = 2^usize::BITS - 1`, `usize::BITS >= 16`, the max
   1102                     // value of `len` is `usize::BITS`.
   1103                     // 16 * usize::BITS < 2^usize::BITS for `usize::BITS > 6`.
   1104                     set: String::with_capacity(len << 4),
   1105                     skipped_sets_counter: 0,
   1106                     skip_empty_set,
   1107                 }))
   1108             }
   1109             _ => Err(TooManyFeaturesErr),
   1110         }
   1111     }
   1112     /// Resets `self` such that iteration returns to the beginning.
   1113     pub(crate) const fn reset(&mut self) {
   1114         // We don't allow construction of `PowerSet` when the number of
   1115         // features exceeds [`Self::MAX_SET_LEN`], so this won't `panic`.
   1116         self.idx = Self::len_minus_one(self.feats.len());
   1117         self.has_remaining = true;
   1118         self.skipped_sets_counter = 0;
   1119     }
   1120     /// Writes the next element into `self.buffer` even if the set contains overlapping features.
   1121     #[expect(
   1122         clippy::arithmetic_side_effects,
   1123         reason = "comment justifies correctness"
   1124     )]
   1125     fn inner_next_set(&mut self) {
   1126         self.buffer.clear();
   1127         self.feats.iter().enumerate().fold((), |(), (i, feat)| {
   1128             if self.idx & (1 << i) != 0 {
   1129                 self.buffer.push(feat.0.as_str());
   1130             }
   1131         });
   1132         if self.idx == 0 {
   1133             self.has_remaining = false;
   1134         // The empty set is always the last set.
   1135         } else if self.idx == 1 && self.skip_empty_set {
   1136             self.idx = 0;
   1137             self.has_remaining = false;
   1138         } else {
   1139             // This won't underflow since `idx > 0`.
   1140             self.idx -= 1;
   1141         }
   1142     }
   1143     /// Transforms the current element into its string form.
   1144     fn current_set(&mut self) {
   1145         self.set.clear();
   1146         self.buffer.iter().fold((), |(), s| {
   1147             self.set.push_str(s);
   1148             self.set.push(',');
   1149         });
   1150         // We remove the trailing comma. In the event `self.set` is empty, this does nothing.
   1151         _ = self.set.pop();
   1152     }
   1153     /// Returns the next set.
   1154     ///
   1155     /// This returns `None` iff there are no more sets to return. It will continue to return `None`
   1156     /// unless [`Self::reset`] is called.
   1157     pub(crate) fn next_set(&mut self) -> Option<&str> {
   1158         self.next_set_with_skip_count().map(|tup| tup.0)
   1159     }
   1160     /// Returns the next set along with the number of set skipped thus far.
   1161     ///
   1162     /// This returns `None` iff there are no more sets to return. It will continue to return `None`
   1163     /// unless [`Self::reset`] is called.
   1164     #[expect(
   1165         clippy::arithmetic_side_effects,
   1166         reason = "comment justifies correctness"
   1167     )]
   1168     pub(crate) fn next_set_with_skip_count(&mut self) -> Option<(&str, usize)> {
   1169         if self.has_remaining {
   1170             if self.check_overlap {
   1171                 while self.has_remaining {
   1172                     self.inner_next_set();
   1173                     if pairwise_disconnected(self.buffer.as_slice(), self.feats) {
   1174                         self.current_set();
   1175                         return Some((&self.set, self.skipped_sets_counter));
   1176                     }
   1177                     // This maxes at `usize::MAX` since we ensure a power set is not created on a
   1178                     // set with more than `usize::BITS` elements.
   1179                     // We set to set every time [`Self::reset`] is called as well.
   1180                     self.skipped_sets_counter += 1;
   1181                 }
   1182                 None
   1183             } else {
   1184                 self.inner_next_set();
   1185                 self.current_set();
   1186                 Some((&self.set, self.skipped_sets_counter))
   1187             }
   1188         } else {
   1189             None
   1190         }
   1191     }
   1192 }
   1193 /// Dependency table.
   1194 enum DepTable {
   1195     /// Library dependencies.
   1196     Dependencies,
   1197     /// Build dependencies.
   1198     BuildDependencies,
   1199 }
   1200 impl DepTable {
   1201     /// Returns the string representation of `self`.
   1202     const fn into_str(self) -> &'static str {
   1203         match self {
   1204             Self::Dependencies => "dependencies",
   1205             Self::BuildDependencies => "build-dependencies",
   1206         }
   1207     }
   1208 }
   1209 /// `"dep:"`.
   1210 const DEP: &[u8; 4] = b"dep:";
   1211 /// Returns `true` iff `utf8` begins with [`DEP`].
   1212 fn is_feature_dependency_a_dependency(utf8: &[u8]) -> bool {
   1213     utf8.starts_with(DEP)
   1214 }
   1215 /// Returns `true` iff `name` does not contain a `'/'` nor begins with [`DEP`].
   1216 fn is_feature_dependency_a_feature(name: &str) -> bool {
   1217     let utf8 = name.as_bytes();
   1218     !(utf8.contains(&b'/') || is_feature_dependency_a_dependency(utf8))
   1219 }
   1220 /// Features in Cargo.toml.
   1221 ///
   1222 /// Note this contains a `Vec` instead of a `HashMap` or `BTreeMap` since we enforce that very few entries exist
   1223 /// due to the exponential nature of generating the power set; thus making a `Vec` more efficient and faster.
   1224 /// This size is so small that we don't even sort and perform a binary search.
   1225 ///
   1226 /// The `String` in the tuple represents the name of the feature, and the `Vec` in the tuple represents the
   1227 /// feature dependencies. The feature dependencies will not contain any dependency that contains a `'/'`
   1228 /// but will contain all others. The name of each feature will not contain a `'/'` nor begin with [`DEP`].
   1229 /// Each dependency that is a feature (i.e., does not begin with [`DEP`]) is well-defined (i.e., is a feature
   1230 /// itself) when `false` is passed to [`Self::extract_from_toml`]; when `true` is passed, then feature dependencies
   1231 /// that are features are not verified to be defined in `self` since implied features still have to be added.
   1232 /// Each feature does not contain any cycles nor redundant dependencies.
   1233 ///
   1234 /// One must still add implied features caused from any optional dependencies iff `true` is passed to
   1235 /// [`Self::extract_from_toml`]; after which, one needs to remove all dependencies that are not features and verify
   1236 /// all dependencies that are features are defined in `self` in order for [`PowerSet`] to work correctly. A feature
   1237 /// with name `<dependency>` needs to be added with an empty `Vec` of dependencies iff no features exist that have a
   1238 /// dependency whose name is `"dep:<dependency>"` _and_ there doesn't already exist a feature with that name.
   1239 /// An error MUST NOT happen if there is such a feature since different kinds of dependencies can have the
   1240 /// same name. While this will allow for the situation where a feature is defined with the same name as
   1241 /// an implied feature, that won't matter once `cargo` is run since it will error anyway.
   1242 #[cfg_attr(test, derive(Debug, PartialEq))]
   1243 pub(crate) struct Features(Vec<(String, Vec<String>)>);
   1244 impl Features {
   1245     /// Returns `true` iff `self` contains a feature named `"default"`.
   1246     pub(crate) fn contains_default(&self) -> bool {
   1247         self.0.iter().any(|f| f.0 == "default")
   1248     }
   1249     /// `panic`s with a static message about the existence of a bug in `validate_dependencies`.
   1250     ///
   1251     /// This is used in lieu of `unreachable` in `validate_dependencies`, `check_redundant_features`
   1252     /// and `extract_feature_dependencies`.
   1253     #[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
   1254     fn impossible<T>() -> T {
   1255         unreachable!("there is a bug in manifest::Features::validate_dependencies.")
   1256     }
   1257     /// Verifies dependencies associated with `feature` is valid.
   1258     ///
   1259     /// `dependencies` are assumed to be associated with `feature` which the pair of is checked to be
   1260     /// a key-value pair from `features` iff `allow_implied_features`. This verifies the following:
   1261     ///
   1262     /// * `dependencies` is an array that only contains strings.
   1263     /// * Each string in `dependencies` that does not contain a `'/'` nor begins with [`DEP`] is a key
   1264     ///   in `features` iff `allow_implied_features`.
   1265     /// * `feature` nor any dependency that is a feature in `dependencies` is cyclic.
   1266     ///
   1267     /// `cycle_detection` MUST contain only `feature` when this is called externally.
   1268     ///
   1269     /// This MUST only be called by itself or [`Self::extract_feature_dependencies`].
   1270     fn validate_dependencies<'a>(
   1271         feature: &str,
   1272         dependencies: &'a DeValue<'_>,
   1273         features: &'a Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
   1274         cycle_detection: &mut Vec<&'a str>,
   1275         allow_implied_features: bool,
   1276     ) -> Result<(), FeatureDependenciesErr> {
   1277         if let DeValue::Array(ref info) = *dependencies {
   1278             info.iter().try_fold((), |(), dep_span| {
   1279                 if let DeValue::String(ref dep_name) = *dep_span.get_ref() {
   1280                     if is_feature_dependency_a_feature(dep_name) {
   1281                         if cycle_detection.contains(&dep_name.as_ref()) {
   1282                             Err(FeatureDependenciesErr::CyclicFeature(
   1283                                 dep_name.clone().into_owned(),
   1284                             ))
   1285                         } else if let Some(next_feature) = features.get(dep_name.as_ref()) {
   1286                             cycle_detection.push(dep_name);
   1287                             Self::validate_dependencies(
   1288                                 dep_name,
   1289                                 next_feature.get_ref(),
   1290                                 features,
   1291                                 cycle_detection,
   1292                                 allow_implied_features,
   1293                             )
   1294                             .map(|()| {
   1295                                 // We require calling code to add `feature` before calling this function. We
   1296                                 // always add the most recent feature dependency. Therefore this is not empty.
   1297                                 _ = cycle_detection.pop().unwrap_or_else(Self::impossible);
   1298                             })
   1299                         } else if allow_implied_features {
   1300                             // `dep_name` may be an implied feature which we have yet to add.
   1301                             Ok(())
   1302                         } else {
   1303                             // We require calling code to add `feature` before calling this function. We always
   1304                             // add the most recent feature dependency. Therefore this is not empty.
   1305                             Err(FeatureDependenciesErr::InvalidDependency(
   1306                                 cycle_detection
   1307                                     .pop()
   1308                                     .unwrap_or_else(Self::impossible)
   1309                                     .to_owned(),
   1310                                 dep_name.clone().into_owned(),
   1311                             ))
   1312                         }
   1313                     } else {
   1314                         Ok(())
   1315                     }
   1316                 } else {
   1317                     Err(FeatureDependenciesErr::InvalidDependencyType(
   1318                         feature.to_owned(),
   1319                     ))
   1320                 }
   1321             })
   1322         } else {
   1323             Err(FeatureDependenciesErr::InvalidFeatureType(
   1324                 feature.to_owned(),
   1325             ))
   1326         }
   1327     }
   1328     /// Verifies there are no redundant dependencies that are features in `dependencies`.
   1329     ///
   1330     /// Returns `true` iff there is a redundant dependency.
   1331     ///
   1332     /// This must be called _after_ `validate_dependencies` is called on the same arguments.
   1333     fn check_redundant_dependencies(
   1334         feature: &str,
   1335         dependencies: &DeArray<'_>,
   1336         features: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
   1337         allow_implied_features: bool,
   1338     ) -> bool {
   1339         dependencies.iter().any(|dep_span| {
   1340             if let DeValue::String(ref dep_name) = *dep_span.get_ref() {
   1341                 is_feature_dependency_a_feature(dep_name)
   1342                     && (feature == dep_name
   1343                         || features.get(dep_name.as_ref()).map_or_else(
   1344                             || {
   1345                                 if allow_implied_features {
   1346                                     false
   1347                                 } else {
   1348                                     // We require `validate_dependencies` to be called before this function which
   1349                                     // ensures all features recursively in the `dependencies` are defined as features iff `!allow_implied_features`.
   1350                                     Self::impossible()
   1351                                 }
   1352                             },
   1353                             |next_feature_span| {
   1354                                 Self::check_redundant_dependencies(
   1355                                     feature,
   1356                                     next_feature_span
   1357                                         .get_ref()
   1358                                         .as_array()
   1359                                         // We require `validate_dependencies` to be called before this function
   1360                                         // which ensures all feature dependencies recursively are arrays.
   1361                                         .unwrap_or_else(Self::impossible),
   1362                                     features,
   1363                                     allow_implied_features,
   1364                                 )
   1365                             },
   1366                         ))
   1367             } else {
   1368                 // We require `validate_dependencies` to be called before this function which ensures all
   1369                 // dependencies recursivley in `dependencies` are strings.
   1370                 Self::impossible()
   1371             }
   1372         })
   1373     }
   1374     /// Extracts the feature dependencies associated with `feature`.
   1375     ///
   1376     /// `dependencies` are assumed to be associated with `feature` which the pair of is checked to be
   1377     /// a key-value pair from `features` iff `allow_implied_features`. This verifies the following:
   1378     ///
   1379     /// * `dependencies` is an array that only contains strings.
   1380     /// * Each string in `dependencies` that does not contain a `'/'` nor begins with [`DEP`] is a key
   1381     ///   in `features` iff `allow_implied_features`.
   1382     /// * There is no redundant feature in `dependencies` where "redundant" means the following:
   1383     ///   * There are no cycles (e.g., feature = \["feature"] or feature = \["a"], a = \["b"], b = \["a"]).
   1384     ///   * No unnecessary dependencies that are features (e.g., feature = \["a", "a"] or feature = \["a", "b"]
   1385     ///     a = \["b"], b = \[]).
   1386     /// * There are no duplicate dependencies in `dependencies` that beging with [`DEP`].
   1387     ///
   1388     /// Note since all dependencies that contain a `'/'` are ignored, there may be duplicates of them.
   1389     /// Also when checking for redundant features in `dependencies`, _only_ features are considered; thus
   1390     /// something like the following is allowed: feature = \["dep:a", "a"], a = \["dep:a"].
   1391     ///
   1392     /// This must only be called from [`Self::extract_from_toml`].
   1393     #[expect(
   1394         clippy::arithmetic_side_effects,
   1395         reason = "comment justifies correctness"
   1396     )]
   1397     fn extract_feature_dependencies<'a>(
   1398         feature: &'a str,
   1399         dependencies: &'a DeValue<'_>,
   1400         features: &'a Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
   1401         cycle_buffer: &mut Vec<&'a str>,
   1402         allow_implied_features: bool,
   1403     ) -> Result<Vec<String>, FeatureDependenciesErr> {
   1404         // `Self::validate_dependencies` requires `cycle_buffer` to contain, and only contain, `feature`.
   1405         cycle_buffer.clear();
   1406         cycle_buffer.push(feature);
   1407         Self::validate_dependencies(feature, dependencies, features, cycle_buffer, allow_implied_features).and_then(|()| {
   1408             // `validate_dependencies` ensures `dependencies` is an array.
   1409             let deps = dependencies.as_array().unwrap_or_else(Self::impossible);
   1410             let mut vec_deps = Vec::with_capacity(deps.len());
   1411             deps.iter().enumerate().try_fold((), |(), (idx, dep_span)| if let DeValue::String(ref dep_name) = *dep_span.get_ref() {
   1412                 let dep_utf8 = dep_name.as_bytes();
   1413                 if dep_utf8.contains(&b'/') {
   1414                     Ok(())
   1415                 } else if is_feature_dependency_a_dependency(dep_utf8) {
   1416                     if vec_deps.iter().any(|d| d == dep_name) {
   1417                         Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), dep_name.clone().into_owned()))
   1418                     } else {
   1419                         vec_deps.push(dep_name.clone().into_owned());
   1420                         Ok(())
   1421                     }
   1422                 } else if let Some(next_feature_span) = features.get(dep_name.as_ref()) {
   1423                     // `validate_dependencies` ensures all feature
   1424                     // dependencies recursively are arrays.
   1425                     let feat_info = next_feature_span.get_ref().as_array().unwrap_or_else(Self::impossible);
   1426                     // `idx < deps.iter().len()`; thus this won't overflow.
   1427                     deps.iter().skip(idx + 1).try_fold((), |(), next_dep_span| if let DeValue::String(ref next_dep_name) = *next_dep_span.get_ref() {
   1428                         if is_feature_dependency_a_feature(next_dep_name) {
   1429                             if dep_name == next_dep_name {
   1430                                 Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), dep_name.clone().into_owned()))
   1431                             } else if Self::check_redundant_dependencies(next_dep_name, feat_info, features, allow_implied_features) {
   1432                                 Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), next_dep_name.clone().into_owned()))
   1433                             } else {
   1434                                 features.get(next_dep_name.as_ref()).map_or_else(
   1435                                     || {
   1436                                         if allow_implied_features {
   1437                                             Ok(())
   1438                                         } else {
   1439                                             // `validate_dependencies` ensures all features
   1440                                             // recursively in the feature dependencies are defined
   1441                                             // as features iff `!allow_implied_features`.
   1442                                             Self::impossible()
   1443                                         }
   1444                                     },
   1445                                     |next_dep_feature_span| {
   1446                                         // `validate_dependencies` ensures all feature
   1447                                         // dependencies recursively are arrays.
   1448                                         if Self::check_redundant_dependencies(dep_name, next_dep_feature_span.get_ref().as_array().unwrap_or_else(Self::impossible), features, allow_implied_features) {
   1449                                             Err(FeatureDependenciesErr::RedundantDependency(feature.to_owned(), dep_name.clone().into_owned()))
   1450                                         } else {
   1451                                             Ok(())
   1452                                         }
   1453                                     }
   1454                                 )
   1455                             }
   1456                         } else {
   1457                             Ok(())
   1458                         }
   1459                     } else {
   1460                         // `validate_dependencies` ensures all dependencies recursively in `dependencies` are
   1461                         // strings.
   1462                         Self::impossible()
   1463                     }).map(|()| vec_deps.push(dep_name.clone().into_owned()))
   1464                 } else if allow_implied_features {
   1465                     vec_deps.push(dep_name.clone().into_owned());
   1466                     Ok(())
   1467                 } else {
   1468                     // `validate_dependencies` ensures all features
   1469                     // recursively in `dependencies` are defined as features
   1470                     // iff `!allow_implied_features`.
   1471                     Self::impossible()
   1472                 }
   1473             } else {
   1474                 // `validate_dependencies` ensures all dependencies recursively in `dependencies` are strings.
   1475                 Self::impossible()
   1476             }).map(|()| vec_deps)
   1477         })
   1478     }
   1479     /// Extracts `"features"` from `toml`.
   1480     fn extract_from_toml(
   1481         toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
   1482         allow_implied_features: bool,
   1483     ) -> Result<Self, FeaturesErr> {
   1484         toml.get(FEATURES).map_or_else(
   1485             || Ok(Self(Vec::new())),
   1486             |features_span| {
   1487                 if let DeValue::Table(ref features) = *features_span.get_ref() {
   1488                     let mut cycle_buffer = Vec::with_capacity(features.len());
   1489                     let mut feats = Vec::with_capacity(features.len());
   1490                     features
   1491                         .iter()
   1492                         .try_fold((), |(), (name_span, feature_span)| {
   1493                             let name = name_span.get_ref();
   1494                             if is_feature_dependency_a_feature(name) {
   1495                                 Self::extract_feature_dependencies(
   1496                                     name,
   1497                                     feature_span.get_ref(),
   1498                                     features,
   1499                                     &mut cycle_buffer,
   1500                                     allow_implied_features,
   1501                                 )
   1502                                 .map_err(FeaturesErr::FeatureDependencies)
   1503                                 .map(|deps| {
   1504                                     feats.push((name.clone().into_owned(), deps));
   1505                                 })
   1506                             } else {
   1507                                 Err(FeaturesErr::InvalidName(name.clone().into_owned()))
   1508                             }
   1509                         })
   1510                         .map(|()| Self(feats))
   1511                 } else {
   1512                     Err(FeaturesErr::InvalidType)
   1513                 }
   1514             },
   1515         )
   1516     }
   1517     /// Extracts optional dependencies and adds their corresponding implied feature to `self` iff
   1518     /// `allow_implied_features` and it's appropriate to do so.
   1519     ///
   1520     /// This must only be called from [`Self::add_implied_features`].
   1521     fn add_optional_dependencies(
   1522         &mut self,
   1523         toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
   1524         table: DepTable,
   1525         allow_implied_features: bool,
   1526     ) -> Result<(), DependenciesErr> {
   1527         let table_name = table.into_str();
   1528         toml.get(table_name).map_or(Ok(()), |deps_span| {
   1529             if let DeValue::Table(ref deps) = *deps_span.get_ref() {
   1530                 deps.iter().try_fold((), |(), dep_span| {
   1531                     let dep_name = dep_span.0.get_ref();
   1532                     if is_feature_dependency_a_feature(dep_name) {
   1533                         match *dep_span.1.get_ref() {
   1534                             DeValue::String(_) => Ok(()),
   1535                             DeValue::Table(ref dep_info) => {
   1536                                 dep_info.get(OPTIONAL).map_or(Ok(()), |opt_span| {
   1537                                     if let DeValue::Boolean(ref optional) = *opt_span.get_ref() {
   1538                                         if *optional {
   1539                                             self.0
   1540                                                 .iter()
   1541                                                 .try_fold((), |(), feat| {
   1542                                                     if feat.0 == *dep_name {
   1543                                                         // We already have a feature with the same name,
   1544                                                         // so we don't need to continue.
   1545                                                         Err(())
   1546                                                     } else if feat.1.iter().any(|feat_dep| {
   1547                                                         feat_dep
   1548                                                             .as_bytes()
   1549                                                             .split_at_checked(DEP.len())
   1550                                                             .is_some_and(|(pref, rem)| {
   1551                                                                 pref == DEP
   1552                                                                     && dep_name.as_bytes() == rem
   1553                                                             })
   1554                                                     }) {
   1555                                                         // The feature dependencies contain `"dep:<dep_name>"`,
   1556                                                         // so we don't need to add an implied feature.
   1557                                                         Err(())
   1558                                                     } else {
   1559                                                         // The feature name is not `<dep_name>` and all of
   1560                                                         // feature dependencies of all features are not named
   1561                                                         // `"dep:<dep_name>"`; thus we need to continue our
   1562                                                         // search.
   1563                                                         Ok(())
   1564                                                     }
   1565                                                 })
   1566                                                 .map_or(Ok(()), |()| {
   1567                                                     if allow_implied_features {
   1568                                                         // There is no feature with the name `<dep_name>` nor
   1569                                                         // are there any features that contain a feature
   1570                                                         // dependency named `"dep:<dep_name>"`; thus we must
   1571                                                         // insert an implied feature.
   1572                                                         self.0.push((
   1573                                                             dep_name.clone().into_owned(),
   1574                                                             Vec::new(),
   1575                                                         ));
   1576                                                         Ok(())
   1577                                                     } else {
   1578                                                         Err(DependenciesErr::ImpliedFeature(
   1579                                                             table_name,
   1580                                                             dep_name.clone().into_owned(),
   1581                                                         ))
   1582                                                     }
   1583                                                 })
   1584                                         } else {
   1585                                             Ok(())
   1586                                         }
   1587                                     } else {
   1588                                         Err(DependenciesErr::OptionalType(
   1589                                             table_name,
   1590                                             dep_name.clone().into_owned(),
   1591                                         ))
   1592                                     }
   1593                                 })
   1594                             }
   1595                             DeValue::Integer(_)
   1596                             | DeValue::Float(_)
   1597                             | DeValue::Boolean(_)
   1598                             | DeValue::Datetime(_)
   1599                             | DeValue::Array(_) => Err(DependenciesErr::DependencyType(
   1600                                 table_name,
   1601                                 dep_name.clone().into_owned(),
   1602                             )),
   1603                         }
   1604                     } else {
   1605                         Err(DependenciesErr::Name(
   1606                             table_name,
   1607                             dep_name.clone().into_owned(),
   1608                         ))
   1609                     }
   1610                 })
   1611             } else {
   1612                 Err(DependenciesErr::Type(table_name))
   1613             }
   1614         })
   1615     }
   1616     /// Adds implied features to `self` based on the optional dependencies in `toml` iff `allow_implied_features`.
   1617     fn add_implied_features(
   1618         &mut self,
   1619         toml: &Map<Spanned<Cow<'_, str>>, Spanned<DeValue<'_>>>,
   1620         allow_implied_features: bool,
   1621     ) -> Result<(), ImpliedFeaturesErr> {
   1622         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(
   1623             || Ok(()),
   1624             |target_span| {
   1625                 if let DeValue::Table(ref target) = *target_span.get_ref() {
   1626                     target.iter().try_fold((), |(), target_platform_span| {
   1627                         if let DeValue::Table(ref target_platform) = *target_platform_span.1.get_ref() {
   1628                             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)))
   1629                         } else {
   1630                             Err(ImpliedFeaturesErr::TargetPlatformType(target_platform_span.0.get_ref().clone().into_owned()))
   1631                         }
   1632                     })
   1633                 } else {
   1634                     Err(ImpliedFeaturesErr::TargetType)
   1635                 }
   1636             }
   1637         ).and_then(|()| {
   1638             if allow_implied_features {
   1639                 // We don't have to worry about cyclic features or anything other than the lack of a feature with
   1640                 // the name of the feature dependency.
   1641                 self.0.iter().try_fold((), |(), feature| feature.1.iter().try_fold((), |(), dep| {
   1642                      // We didn't save any feature dependencies that contain `'/'`, so we simply have to check if
   1643                      // a dependency begins with [`DEP`] to skip it.
   1644                      if is_feature_dependency_a_dependency(dep.as_bytes()) || self.0.iter().any(|other_feature| other_feature.0 == *dep) {
   1645                          Ok(())
   1646                      } else {
   1647                          Err(ImpliedFeaturesErr::InvalidDependency(feature.0.clone(), dep.clone()))
   1648                      }
   1649                  }))
   1650             } else {
   1651                 // When `!allowed_implied_features`, [`Self::validate_dependencies`] verifies non-dependency
   1652                 // feature dependencies are defined as features.
   1653                 Ok(())
   1654             }
   1655         })))
   1656     }
   1657     /// Returns the power set of `self` with semantically equivalent sets removed.
   1658     ///
   1659     /// The empty set of features is skipped iff `skip_no_feats`. None is only
   1660     /// returned if there are no sets of features. This is only possible iff
   1661     /// `self` contains no features and `skip_no_feats`.
   1662     pub(crate) fn power_set(
   1663         &self,
   1664         skip_no_feats: bool,
   1665     ) -> Result<Option<PowerSet<'_>>, TooManyFeaturesErr> {
   1666         PowerSet::new(self, skip_no_feats)
   1667     }
   1668 }
   1669 /// Package and features in `Cargo.toml`.
   1670 #[cfg_attr(test, derive(Debug, PartialEq))]
   1671 pub(crate) struct Manifest {
   1672     /// The package.
   1673     package: Package,
   1674     /// The features.
   1675     features: Features,
   1676 }
   1677 impl Manifest {
   1678     /// Returns the defined MSRV iff there was one defined.
   1679     pub(crate) const fn package(&self) -> &Package {
   1680         &self.package
   1681     }
   1682     /// Returns the defined features.
   1683     ///
   1684     /// Note the returned `Features` doesn't have any cyclic features, each feature dependency for a given
   1685     /// feature is a feature itself, and there are no redundant feature dependencies for a given feature.
   1686     pub(crate) const fn features(&self) -> &Features {
   1687         &self.features
   1688     }
   1689     /// Returns the data needed from `Cargo.toml`.
   1690     ///
   1691     /// Note `ignore_features` MUST not contain an empty `String`.
   1692     #[expect(
   1693         clippy::arithmetic_side_effects,
   1694         reason = "comments justify correctness"
   1695     )]
   1696     #[expect(
   1697         clippy::needless_pass_by_value,
   1698         reason = "want to drop `val` as soon as possible"
   1699     )]
   1700     pub(crate) fn from_toml(
   1701         val: String,
   1702         allow_implied_features: bool,
   1703         cargo_toml: &Path,
   1704         ignore_features: &[String],
   1705     ) -> Result<Self, Box<ManifestErr>> {
   1706         Map::parse(val.as_str())
   1707             .map_err(|e| Box::new(ManifestErr::Toml(e, cargo_toml.to_path_buf())))
   1708             .and_then(|span| {
   1709                 let cargo = span.get_ref();
   1710                 Package::extract_from_toml(cargo, cargo_toml)
   1711                     .map_err(|e| Box::new(ManifestErr::Package(e, cargo_toml.to_path_buf())))
   1712                     .and_then(|package| {
   1713                         Features::extract_from_toml(cargo, allow_implied_features)
   1714                             .map_err(|e| {
   1715                                 Box::new(ManifestErr::Features(e, cargo_toml.to_path_buf()))
   1716                             })
   1717                             .and_then(|mut features| {
   1718                                 features
   1719                                     .add_implied_features(cargo, allow_implied_features)
   1720                                     .map_err(|e| {
   1721                                         Box::new(ManifestErr::ImpliedFeatures(
   1722                                             e,
   1723                                             cargo_toml.to_path_buf(),
   1724                                         ))
   1725                                     })
   1726                                     .and_then(|()| {
   1727                                         features.0.iter_mut().fold(
   1728                                             (),
   1729                                             |(), &mut (_, ref mut deps)| {
   1730                                                 deps.retain(|d| {
   1731                                                     // We retain only features. Since we didn't save any
   1732                                                     // dependencies that contain `'/'`, it's slightly faster to just
   1733                                                     // check that a feature dependency is not a dependency.
   1734                                                     !is_feature_dependency_a_dependency(
   1735                                                         d.as_bytes(),
   1736                                                     )
   1737                                                 });
   1738                                             },
   1739                                         );
   1740                                         // First we ensure all features we are to ignore are defined;
   1741                                         // while doing this, we remove the feature. Note `ignore_features`
   1742                                         // and `features` only contain distinct features, so we simply
   1743                                         // have to check for the first occurrence.
   1744                                         // Note calling code is required to have removed the empty
   1745                                         // string if it had existed.
   1746                                         ignore_features
   1747                                             .iter()
   1748                                             .try_fold((), |(), ig_feat| {
   1749                                                 features
   1750                                                     .0
   1751                                                     .iter()
   1752                                                     .try_fold(0, |idx, info| {
   1753                                                         if info.0 == *ig_feat {
   1754                                                             Err(idx)
   1755                                                         } else {
   1756                                                             // Clearly free from overflow.
   1757                                                             Ok(idx + 1)
   1758                                                         }
   1759                                                     })
   1760                                                     .map_or_else(
   1761                                                         |idx| {
   1762                                                             drop(features.0.swap_remove(idx));
   1763                                                             Ok(())
   1764                                                         },
   1765                                                         |_| {
   1766                                                             Err(Box::new(
   1767                                                                 ManifestErr::UndefinedIgnoreFeature(
   1768                                                                     ig_feat.clone(),
   1769                                                                     cargo_toml.to_path_buf(),
   1770                                                                 ),
   1771                                                             ))
   1772                                                         },
   1773                                                     )
   1774                                             })
   1775                                             .map(|()| {
   1776                                                 // Now we remove all features that depend on the
   1777                                                 // features we are to ignore.
   1778                                                 ignore_features.iter().fold((), |(), ig_feat| {
   1779                                                     features.0.retain(|info| {
   1780                                                         !info.1.iter().any(|d| d == ig_feat)
   1781                                                     });
   1782                                                 });
   1783                                                 Self { package, features }
   1784                                             })
   1785                                     })
   1786                             })
   1787                     })
   1788             })
   1789     }
   1790 }