cargo-features-manager 0.12.0

A tui tool to enable/disable & prune dependency features
use cargo_metadata::{CargoOpt, PackageId};
use console::style;
use indicatif::ProgressBar;

use crate::io::parsing::workspace::parse_workspace;
use color_eyre::Result;

use crate::io::parsing::dependency::parse_dependency;
use crate::io::util::toml_document_from_path;
use crate::project::dependency::Dependency;
use crate::project::package::Package;
use color_eyre::eyre::ContextCompat;
use semver::VersionReq;
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;

pub fn get_packages(path: impl Into<PathBuf>) -> Result<(Vec<Package>, Option<Package>, PathBuf)> {
    let progress = ProgressBar::new_spinner()
        .with_message(format!("running '{}'", style("cargo metadata").dim()));
    progress.enable_steady_tick(Duration::from_millis(100));

    let metadata = cargo_metadata::MetadataCommand::new()
        .current_dir(path)
        .features(CargoOpt::AllFeatures)
        .exec()?;

    progress.finish_and_clear();

    let metadata_packages: HashMap<PackageId, cargo_metadata::Package> = metadata
        .packages
        .into_iter()
        .map(|package| (package.id.clone(), package))
        .collect();

    let packages = metadata
        .workspace_members
        .iter()
        .map(|package| parse_package(package, &metadata_packages))
        .collect::<Result<Vec<Package>>>()?;

    Ok((
        packages,
        parse_workspace(metadata.workspace_root.as_str(), &metadata_packages)?,
        metadata.workspace_root.into(),
    ))
}

pub fn parse_package(
    package: &PackageId,
    packages: &HashMap<PackageId, cargo_metadata::Package>,
) -> Result<Package> {
    let package = packages.get(package).context("package not found")?;

    let toml_doc = toml_document_from_path(package.manifest_path.as_str())?;

    let dependencies: Result<Vec<Dependency>> = package
        .dependencies
        .iter()
        .map(|dep| parse_dependency(dep, packages, &toml_doc))
        .collect();

    Ok(Package {
        dependencies: dependencies?,
        name: package.name.to_string(),
        manifest_path: package.manifest_path.to_string(),
    })
}

pub fn get_package_from_version<'a>(
    name: &str,
    version_req: &VersionReq,
    packages: &'a HashMap<PackageId, cargo_metadata::Package>,
) -> Result<&'a cargo_metadata::Package> {
    packages
        .values()
        .filter(|package| package.name == name)
        .find(|package| version_req.matches(&package.version) || version_req.to_string() == "*")
        .context(format!(
            "could not find version for {} {}",
            name, version_req
        ))
}