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
))
}