use std::path::{Path, PathBuf};
use crate::bindgen::cargo::cargo_expand;
use crate::bindgen::cargo::cargo_lock::{self, Lock};
pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
use crate::bindgen::cargo::cargo_toml;
use crate::bindgen::config::Profile;
use crate::bindgen::error::Error;
use crate::bindgen::ir::Cfg;
fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
let split: Vec<&str> = dep_string.split_whitespace().collect();
(split[0], split.get(1).cloned())
}
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
manifest_path: PathBuf,
binding_crate_name: String,
lock: Option<Lock>,
metadata: Metadata,
clean: bool,
}
impl Cargo {
pub(crate) fn load(
crate_dir: &Path,
lock_file: Option<&str>,
binding_crate_name: Option<&str>,
use_cargo_lock: bool,
clean: bool,
only_target_dependencies: bool,
existing_metadata_file: Option<&Path>,
) -> Result<Cargo, Error> {
let toml_path = crate_dir.join("Cargo.toml");
let metadata =
cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
.map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
let lock_path = lock_file
.map(PathBuf::from)
.unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));
let lock = if use_cargo_lock {
match cargo_lock::lock(&lock_path) {
Ok(lock) => Some(lock),
Err(x) => {
warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
None
}
}
} else {
None
};
let binding_crate_name = match binding_crate_name {
Some(s) => s.to_owned(),
None => {
let manifest = cargo_toml::manifest(&toml_path)
.map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
manifest.package.name
}
};
Ok(Cargo {
manifest_path: toml_path,
binding_crate_name,
lock,
metadata,
clean,
})
}
pub(crate) fn binding_crate_name(&self) -> &str {
&self.binding_crate_name
}
pub(crate) fn binding_crate_ref(&self) -> PackageRef {
match self.find_pkg_ref(&self.binding_crate_name) {
Some(pkg_ref) => pkg_ref,
None => panic!(
"Unable to find {} for {:?}",
self.binding_crate_name, self.manifest_path
),
}
}
pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
let lock = match self.lock {
Some(ref lock) => lock,
None => return vec![],
};
let mut dependencies = None;
if let Some(ref root) = lock.root {
if root.name == package.name
&& package
.version
.as_ref()
.map_or(true, |v| *v == root.version)
{
dependencies = root.dependencies.as_ref();
}
}
if dependencies.is_none() {
if let Some(ref lock_packages) = lock.package {
for lock_package in lock_packages {
if lock_package.name == package.name
&& package
.version
.as_ref()
.map_or(true, |v| *v == lock_package.version)
{
dependencies = lock_package.dependencies.as_ref();
break;
}
}
}
}
if dependencies.is_none() {
return vec![];
}
dependencies
.unwrap()
.iter()
.map(|dep| {
let (dep_name, dep_version) = parse_dep_string(dep);
let dep_version = dep_version.or_else(|| {
let mut versions = self.metadata.packages.iter().filter_map(|package| {
if package.name_and_version.name != dep_name {
return None;
}
package.name_and_version.version.as_deref()
});
let version = versions.next();
if versions.next().is_none() {
version
} else {
warn!("when looking for a version for package {}, multiple versions where found", dep_name);
None
}
});
let cfg = self
.metadata
.packages
.get(package)
.and_then(|meta_package| meta_package.dependencies.get(dep_name))
.and_then(Cfg::load_metadata);
let package_ref = PackageRef {
name: dep_name.to_owned(),
version: dep_version.map(|v| v.to_owned()),
};
(package_ref, cfg)
})
.collect()
}
fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
for package in &self.metadata.packages {
if package.name_and_version.name == package_name {
return Some(package.name_and_version.clone());
}
}
None
}
#[allow(unused)]
pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
self.metadata
.packages
.get(package)
.and_then(|meta_package| {
Path::new(&meta_package.manifest_path)
.parent()
.map(|x| x.to_owned())
})
}
pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
let kind_lib = String::from("lib");
let kind_staticlib = String::from("staticlib");
let kind_rlib = String::from("rlib");
let kind_cdylib = String::from("cdylib");
let kind_dylib = String::from("dylib");
self.metadata
.packages
.get(package)
.and_then(|meta_package| {
for target in &meta_package.targets {
if target.kind.contains(&kind_lib)
|| target.kind.contains(&kind_staticlib)
|| target.kind.contains(&kind_rlib)
|| target.kind.contains(&kind_cdylib)
|| target.kind.contains(&kind_dylib)
{
return Some(PathBuf::from(&target.src_path));
}
}
None
})
}
pub(crate) fn expand_crate(
&self,
package: &PackageRef,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
profile: Profile,
) -> Result<String, cargo_expand::Error> {
cargo_expand::expand(
&self.manifest_path,
&package.name,
package.version.as_deref(),
self.clean,
expand_all_features,
expand_default_features,
expand_features,
profile,
)
}
}