use crate::{
error::Fallible,
event,
internal::dag::DepGraph,
package::{query, Package},
Error, Session,
};
pub(crate) fn resolve_dependencies(session: &Session, packages: &mut Vec<Package>) -> Fallible<()> {
let mut graph = DepGraph::<String>::new();
let mut to_resolve = packages.clone();
let synced = query::query_synced(session, &["*"], &[])?;
loop {
if to_resolve.is_empty() {
break;
}
let mut tmp = vec![];
tmp.append(&mut to_resolve);
for pkg in tmp.into_iter() {
let mut resolved = vec![];
let deps = pkg.dependencies();
if deps.is_empty() {
graph.register_node(pkg.name().to_owned());
} else {
let queries = deps.iter().map(|d| d.as_str());
for query in queries {
let mut matched = synced
.iter()
.filter(|p| {
let (query_bucket, query_name) =
query.split_once('/').unwrap_or(("", query));
let bucket_matched =
query_bucket.is_empty() || p.bucket() == query_bucket;
let name_matched = p.name() == query_name;
bucket_matched && name_matched
})
.cloned()
.collect::<Vec<_>>();
match matched.len() {
0 => return Err(Error::PackageNotFound(query.to_owned())),
1 => {
let p = matched.pop().unwrap();
if !(resolved.contains(&p)
|| to_resolve.contains(&p)
|| packages.contains(&p))
{
resolved.push(p);
}
}
_ => {
let (installed_candidate, mut matched) = matched
.into_iter()
.partition::<Vec<_>, _>(|p| p.is_strictly_installed());
if !installed_candidate.is_empty() {
matched = installed_candidate;
} else {
select_candidate(session, &mut matched)?;
}
let p = matched.pop().unwrap();
if !(resolved.contains(&p)
|| to_resolve.contains(&p)
|| packages.contains(&p))
{
resolved.push(p);
}
}
}
}
let dep_nodes = resolved
.iter()
.map(|p: &Package| p.name().to_owned())
.collect::<Vec<_>>();
graph.register_deps(pkg.name().to_owned(), dep_nodes);
}
graph.check()?;
to_resolve.append(&mut resolved);
}
packages.extend(to_resolve.clone());
}
packages.reverse();
Ok(())
}
pub(crate) fn select_candidate(session: &Session, candidates: &mut Vec<Package>) -> Fallible<()> {
let name = candidates[0].name().to_owned();
candidates.sort_by_key(|p| p.ident());
if let Some(tx) = session.emitter() {
let question = candidates.iter().map(|p| p.ident()).collect::<Vec<_>>();
if tx
.send(event::Event::PromptPackageCandidate(question))
.is_ok()
{
let rx = session.receiver().unwrap();
while let Ok(answer) = rx.recv() {
if let event::Event::PromptPackageCandidateResult(idx) = answer {
if idx < candidates.len() {
*candidates = vec![candidates[idx].clone()];
return Ok(());
}
return Err(Error::InvalidAnswer);
}
}
}
}
Err(Error::PackageMultipleCandidates(name))
}
pub(crate) fn resolve_cascade(
session: &Session,
packages: &mut Vec<Package>,
escape_hold: bool,
) -> Fallible<()> {
let mut to_resolve = packages.clone();
let installed = query::query_installed(session, &["*"], &[])?;
loop {
if to_resolve.is_empty() {
break;
}
let tmp = to_resolve.clone();
to_resolve = vec![];
for pkg in tmp.into_iter() {
let mut unneeded = vec![];
let dep_names = pkg
.dependencies()
.into_iter()
.map(super::extract_name)
.collect::<Vec<_>>();
for dep_name in dep_names {
let mut result = installed
.iter()
.filter(|p| p.name() == dep_name)
.collect::<Vec<_>>();
if result.is_empty() {
continue;
}
assert_eq!(result.len(), 1);
let dep_pkg = result.pop().unwrap();
let mut dependents = vec![];
installed.iter().for_each(|p| {
let be_dependent = p
.dependencies()
.iter()
.map(super::extract_name)
.any(|d| d == dep_pkg.name());
if be_dependent {
dependents.push(p.clone());
}
});
dependents.retain(|p| p.name() != pkg.name());
let needed = dependents
.iter()
.any(|p| !packages.contains(p) && !unneeded.contains(p));
if !needed {
if dep_pkg.is_held() && !escape_hold {
return Err(Error::PackageCascadeRemoveHold(dep_pkg.name().to_owned()));
}
unneeded.push(dep_pkg.to_owned());
}
}
unneeded.dedup();
to_resolve.append(&mut unneeded);
}
packages.extend(to_resolve.clone());
}
packages.dedup();
Ok(())
}