use super::manifest::CargoToml;
use super::types::PlannedCompilationUnit;
use std::collections::HashMap;
use std::path::PathBuf;
pub fn resolve_extern_crates(
unit: &PlannedCompilationUnit,
completed: &HashMap<String, PathBuf>,
) -> Vec<(String, PathBuf)> {
resolve_extern_crates_with_target(unit, completed, unit.for_host)
}
pub fn resolve_extern_crates_for_build_script(
unit: &PlannedCompilationUnit,
completed: &HashMap<String, PathBuf>,
) -> Vec<(String, PathBuf)> {
resolve_extern_crates_with_target(unit, completed, true)
}
pub fn resolve_extern_crates_with_target(
unit: &PlannedCompilationUnit,
completed: &HashMap<String, PathBuf>,
for_host: bool,
) -> Vec<(String, PathBuf)> {
let pkg_toml = CargoToml::from_file(&unit.manifest_path).unwrap_or_default();
let aliases = pkg_toml.dependency_aliases();
let mut extern_crates = Vec::new();
let mut sorted_deps = unit.dependencies.clone();
sorted_deps.sort_by_key(|dep_id| {
let is_host_dep = dep_id.ends_with(":host");
if for_host {
if is_host_dep {
0
} else {
1
}
} else {
if !is_host_dep {
0
} else {
1
}
}
});
for dep_id in &sorted_deps {
let fallback_id = if dep_id.ends_with(":host") {
dep_id.strip_suffix(":host").map(|s| s.to_string())
} else {
Some(format!("{}:host", dep_id))
};
if let Some(artifact_path) = completed
.get(dep_id)
.or_else(|| fallback_id.as_ref().and_then(|f| completed.get(f)))
{
let clean_dep_id = dep_id.strip_suffix(":host").unwrap_or(dep_id);
let dep_pkg = clean_dep_id.split("::").next().unwrap_or(clean_dep_id);
let pkg_name_only = dep_pkg.split('@').next().unwrap_or(dep_pkg);
let dep_version = dep_pkg.split('@').nth(1);
let dep_crate = if let Some(rest) = clean_dep_id.rsplit(':').next() {
rest
} else {
pkg_name_only
};
let norm_pkg = pkg_name_only.replace('-', "_");
let mut matched_aliases = Vec::new();
if let Some(alias_list) = aliases
.real_to_aliases
.get(pkg_name_only)
.or_else(|| aliases.real_to_aliases.get(&norm_pkg))
{
for (alias, ver_req) in alias_list {
let matches = match (ver_req, dep_version) {
(Some(req_str), Some(ver_str)) => {
if let (Ok(req), Ok(ver)) = (
semver::VersionReq::parse(req_str),
semver::Version::parse(ver_str),
) {
req.matches(&ver)
} else {
ver_str.starts_with(req_str) || req_str == ver_str
}
}
_ => true,
};
if matches {
matched_aliases.push(alias.clone());
}
}
}
let is_linking_kind =
matches!(
unit.kind.as_str(),
"bin"
| "test"
| "custom-test"
| "proc-macro"
| "dylib"
| "cdylib"
| "example"
| "bench"
) || super::pipelining::package_has_build_script(&unit.manifest_path);
let chosen_artifact = if !is_linking_kind {
let rmeta = artifact_path.with_extension("rmeta");
if rmeta.is_file() {
rmeta
} else {
artifact_path.clone()
}
} else {
artifact_path.clone()
};
let self_crate_name = if unit.kind == "test" && unit.target_name.ends_with("_unit_test")
{
unit.package_name.replace('-', "_")
} else {
unit.target_name.replace('-', "_")
};
let mut add_extern = |name: &str, path: &PathBuf| {
let clean = name.replace('-', "_");
if clean == self_crate_name {
return;
}
if !extern_crates
.iter()
.any(|(n, _): &(String, PathBuf)| n == &clean)
{
extern_crates.push((clean, path.clone()));
}
};
if matched_aliases.is_empty() {
add_extern(dep_crate, &chosen_artifact);
if dep_crate != norm_pkg {
add_extern(&norm_pkg, &chosen_artifact);
}
} else {
for alias in matched_aliases {
add_extern(&alias, &chosen_artifact);
}
add_extern(dep_crate, &chosen_artifact);
if dep_crate != norm_pkg {
add_extern(&norm_pkg, &chosen_artifact);
}
}
}
}
extern_crates
}