use std::collections::BTreeMap;
use std::sync::Arc;
use crate::resolution::rust::dependency::CargoDependencyKind;
use crate::resolution::rust::edition::CargoEdition;
use crate::resolution::rust::identity::{PackageId, TargetId, position};
use crate::resolution::rust::package::RustPackage;
use crate::resolution::rust::project::RustProject;
use crate::resolution::rust::target::{CargoTargetKind, RustTarget};
use super::activation::{Predicate, conjoin, disjoin};
use super::error::{
ClosureSite, ResolutionLimit, RustSnapshotError, SourceClosureFailure, SourceClosureFailureKind,
};
use super::failure::{failure, limit_failure};
use super::selection_chain::Ancestry;
use super::selection_edge::{self, SelectedEdge};
struct UnitIdentity {
package: PackageId,
package_name: Arc<str>,
target: TargetId,
target_name: Arc<str>,
entry: Arc<str>,
manifest: Arc<str>,
kind: CargoTargetKind,
edition: CargoEdition,
}
pub(super) struct UnitDraft {
pub(super) package: PackageId,
pub(super) target: TargetId,
pub(super) target_name: Arc<str>,
pub(super) manifest: Arc<str>,
pub(super) kind: CargoTargetKind,
pub(super) entry: Arc<str>,
pub(super) predicate: Predicate,
pub(super) edition: CargoEdition,
}
pub(super) struct EdgeDraft {
pub(super) source: u32,
pub(super) target: u32,
pub(super) name: Arc<str>,
pub(super) kind: CargoDependencyKind,
pub(super) predicate: Predicate,
}
pub(super) struct Selection {
pub(super) units: Box<[UnitDraft]>,
pub(super) edges: Box<[EdgeDraft]>,
pub(super) root: u32,
}
type UnitSortKey = (Arc<str>, CargoTargetKind, Arc<str>);
type EdgeKey = (TargetId, CargoDependencyKind, Arc<str>, TargetId);
struct Pending {
identity: UnitIdentity,
predicate: Predicate,
chain: Ancestry,
depth: u32,
origin: ClosureSite,
}
struct Selector<'a> {
project: &'a RustProject,
root_target: TargetId,
root_kind: CargoTargetKind,
units: BTreeMap<TargetId, (UnitSortKey, UnitDraft)>,
edges: BTreeMap<EdgeKey, Predicate>,
}
pub(super) fn select(
project: &RustProject,
root: &RustTarget,
) -> Result<Selection, RustSnapshotError> {
let package = project
.package(root.package())
.ok_or(RustSnapshotError::UnknownTarget {
index: root.package().index(),
})?;
let identity = identity_of(package, root);
let chain = Ancestry::root(identity.target, &identity.package_name);
let mut selector = Selector {
project,
root_target: root.id(),
root_kind: root.kind(),
units: BTreeMap::new(),
edges: BTreeMap::new(),
};
let mut pending = vec![Pending {
origin: ClosureSite::Target {
name: Box::from(root.name()),
entry: Box::from(root.entry_path()),
},
identity,
predicate: Predicate::Always,
chain,
depth: 0,
}];
while let Some(item) = pending.pop() {
pending.extend(selector.visit(item)?);
}
selector.finish()
}
fn identity_of(package: &RustPackage, target: &RustTarget) -> UnitIdentity {
UnitIdentity {
package: package.id(),
package_name: Arc::clone(&package.name),
target: target.id(),
target_name: Arc::clone(&target.name),
entry: Arc::clone(&target.entry_path),
manifest: Arc::clone(&package.manifest_path),
kind: target.kind(),
edition: package.edition(),
}
}
impl Selector<'_> {
fn visit(&mut self, item: Pending) -> Result<Vec<Pending>, RustSnapshotError> {
self.check_chain(&item)?;
self.check_depth(&item)?;
match self.merge(&item)? {
true => self.children(&item),
false => Ok(Vec::new()),
}
}
fn check_chain(&self, item: &Pending) -> Result<(), RustSnapshotError> {
match item.chain.repeats_latest() {
true => Err(refuse(cycle_failure(item))),
false => Ok(()),
}
}
fn check_depth(&self, item: &Pending) -> Result<(), RustSnapshotError> {
let ceiling = self.project.limits().max_dependency_depth;
match item.depth > ceiling {
true => Err(refuse(limit_failure(
ResolutionLimit::DependencyDepth,
(&item.origin, None),
ceiling.into(),
))),
false => Ok(()),
}
}
fn merge(&mut self, item: &Pending) -> Result<bool, RustSnapshotError> {
let existing = self
.units
.get(&item.identity.target)
.map(|(_, draft)| draft.predicate.clone());
let merged = match &existing {
Some(current) => disjoin(current, &item.predicate),
None => item.predicate.clone(),
};
if existing.as_ref() == Some(&merged) {
return Ok(false);
}
self.insert(&item.identity, merged);
self.check_capacity(item)?;
Ok(true)
}
fn insert(&mut self, identity: &UnitIdentity, predicate: Predicate) {
let key = (
Arc::clone(&identity.manifest),
identity.kind,
Arc::clone(&identity.target_name),
);
let draft = UnitDraft {
package: identity.package,
target: identity.target,
target_name: Arc::clone(&identity.target_name),
manifest: Arc::clone(&identity.manifest),
kind: identity.kind,
entry: Arc::clone(&identity.entry),
predicate,
edition: identity.edition,
};
self.units.insert(identity.target, (key, draft));
}
fn check_capacity(&self, item: &Pending) -> Result<(), RustSnapshotError> {
let ceiling = self.project.limits().max_units;
match u64::try_from(self.units.len()).unwrap_or(u64::MAX) > u64::from(ceiling) {
true => Err(refuse(limit_failure(
ResolutionLimit::Units,
(&item.origin, None),
ceiling.into(),
))),
false => Ok(()),
}
}
fn children(&mut self, item: &Pending) -> Result<Vec<Pending>, RustSnapshotError> {
let root = (item.identity.target == self.root_target, self.root_kind);
let selected =
selection_edge::select(self.project, item.identity.package, root).map_err(refuse)?;
let mut next = Vec::new();
for edge in selected.iter() {
next.push(self.follow(item, edge));
}
Ok(next)
}
fn follow(&mut self, item: &Pending, edge: &SelectedEdge<'_>) -> Pending {
let predicate = edge.predicate();
let key = (
item.identity.target,
edge.kind(),
Arc::clone(edge.shared_name()),
edge.target().id(),
);
self.edges.insert(key, predicate.clone());
let identity = identity_of(edge.package(), edge.target());
let chain = item.chain.extend(identity.target, &identity.package_name);
Pending {
predicate: conjoin(&item.predicate, &predicate),
chain,
depth: item.depth.saturating_add(1),
origin: edge.site(self.project),
identity,
}
}
fn finish(self) -> Result<Selection, RustSnapshotError> {
let mut ordered: Vec<(UnitSortKey, UnitDraft)> = self.units.into_values().collect();
ordered.sort_by(|left, right| left.0.cmp(&right.0));
let positions: BTreeMap<TargetId, u32> = ordered
.iter()
.enumerate()
.map(|(index, (_, draft))| (draft.target, position(index)))
.collect();
let root =
positions
.get(&self.root_target)
.copied()
.ok_or(RustSnapshotError::UnknownTarget {
index: self.root_target.index(),
})?;
Ok(Selection {
edges: ordered_edges(&self.edges, &positions)?,
units: ordered
.into_iter()
.map(|(_, draft)| draft)
.collect::<Vec<_>>()
.into_boxed_slice(),
root,
})
}
}
fn ordered_edges(
edges: &BTreeMap<EdgeKey, Predicate>,
positions: &BTreeMap<TargetId, u32>,
) -> Result<Box<[EdgeDraft]>, RustSnapshotError> {
let mut drafts: Vec<EdgeDraft> = edges
.iter()
.map(|(key, predicate)| edge_draft(key, predicate, positions))
.collect::<Result<_, _>>()?;
drafts.sort_by(|left, right| {
(left.source, left.kind, left.name.as_ref(), left.target).cmp(&(
right.source,
right.kind,
right.name.as_ref(),
right.target,
))
});
Ok(drafts.into_boxed_slice())
}
fn edge_draft(
key: &EdgeKey,
predicate: &Predicate,
positions: &BTreeMap<TargetId, u32>,
) -> Result<EdgeDraft, RustSnapshotError> {
let (source, kind, name, target) = key;
Ok(EdgeDraft {
source: placed(source, positions)?,
target: placed(target, positions)?,
name: Arc::clone(name),
kind: *kind,
predicate: predicate.clone(),
})
}
fn placed(
target: &TargetId,
positions: &BTreeMap<TargetId, u32>,
) -> Result<u32, RustSnapshotError> {
positions
.get(target)
.copied()
.ok_or(RustSnapshotError::UnknownTarget {
index: target.index(),
})
}
fn cycle_failure(item: &Pending) -> SourceClosureFailure {
let names = item.chain.package_names();
let message = format!(
"the dependency chain {} repeats a package",
names.join(" -> ")
);
failure(
SourceClosureFailureKind::DependencyCycle,
(&item.origin, None),
message,
)
}
fn refuse(failure: SourceClosureFailure) -> RustSnapshotError {
RustSnapshotError::SourceClosure(super::error::SourceClosureError::new(
Box::new([]),
Box::new([failure]),
))
}