}
}
+ #[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
+ enum ConflictReason {
+ Semver,
+ Links(String),
+ }
+
+ impl ConflictReason {
+ fn is_links(&self) -> bool {
+ match self {
+ &ConflictReason::Semver => false,
+ &ConflictReason::Links(_) => true,
+ }
+ }
+ }
+
+#[derive(Clone)]
struct BacktrackFrame<'a> {
cur: usize,
context_backup: Context<'a>,
struct RemainingCandidates {
remaining: RcVecIter<Candidate>,
// note: change to RcList or something if clone is to expensive
- conflicting_prev_active: HashSet<PackageId>,
+ conflicting_prev_active: HashMap<PackageId, ConflictReason>,
+ // This is a inlined peekable generator
+ has_another: Option<Candidate>,
}
impl RemainingCandidates {
- fn next(&mut self, prev_active: &[Summary], links: &HashMap<String, PackageId>) -> Result<Candidate, HashMap<PackageId, ConflictReason>> {
+ fn new(candidates: &Rc<Vec<Candidate>>) -> RemainingCandidates {
+ RemainingCandidates {
+ remaining: RcVecIter::new(Rc::clone(candidates)),
- conflicting_prev_active: HashSet::new(),
++ conflicting_prev_active: HashMap::new(),
+ has_another: None,
+ }
+ }
+
- fn next(&mut self, prev_active: &[Summary]) -> Result<(Candidate, bool), HashSet<PackageId>> {
++ fn next(&mut self, prev_active: &[Summary], links: &HashMap<String, PackageId>) -> Result<(Candidate, bool), HashMap<PackageId, ConflictReason>> {
// Filter the set of candidates based on the previously activated
// versions for this dependency. We can actually use a version if it
// precisely matches an activated version or if it is otherwise
// When we are done we return the set of previously activated
// that conflicted with the ones we tried. If any of these change
// then we would have considered different candidates.
+ use std::mem::replace;
for (_, b) in self.remaining.by_ref() {
- if let Some(a) = prev_active.iter().find(|a| compatible(a.version(), b.summary.version())) {
+ if let Some(link) = b.summary.links() {
+ if let Some(a) = links.get(link) {
+ if a != b.summary.package_id() {
+ self.conflicting_prev_active.insert(a.clone(), ConflictReason::Links(link.to_owned()));
+ continue
+ }
+ }
+ }
+ if let Some(a) = prev_active
+ .iter()
+ .find(|a| compatible(a.version(), b.summary.version()))
+ {
if *a != b.summary {
- self.conflicting_prev_active.insert(a.package_id().clone());
+ self.conflicting_prev_active.insert(a.package_id().clone(), ConflictReason::Semver);
- continue
+ continue;
}
}
- return Ok(b);
+ if let Some(r) = replace(&mut self.has_another, Some(b)) {
+ return Ok((r, true));
+ }
}
- Err(self.conflicting_prev_active.clone())
+ replace(&mut self.has_another, None)
+ .map(|r| (r, false))
+ .ok_or_else(|| self.conflicting_prev_active.clone())
}
}
let (mut parent, (mut cur, (mut dep, candidates, mut features))) = frame;
assert!(!remaining_deps.is_empty());
- let (next, has_another, remaining_candidates) = {
- let prev_active = cx.prev_active(&dep);
- trace!("{}[{}]>{} {} candidates", parent.name(), cur, dep.name(),
- candidates.len());
- trace!("{}[{}]>{} {} prev activations", parent.name(), cur,
- dep.name(), prev_active.len());
- let mut candidates = RemainingCandidates {
- remaining: RcVecIter::new(Rc::clone(&candidates)),
- conflicting_prev_active: HashMap::new(),
- };
- (candidates.next(prev_active, &cx.links),
- candidates.clone().next(prev_active, &cx.links).is_ok(),
- candidates)
- };
+ trace!("{}[{}]>{} {} candidates", parent.name(), cur, dep.name(), candidates.len());
+ trace!("{}[{}]>{} {} prev activations", parent.name(), cur, dep.name(), cx.prev_active(&dep).len());
+ let mut remaining_candidates = RemainingCandidates::new(&candidates);
- let next = remaining_candidates.next(cx.prev_active(&dep));
++ let next = remaining_candidates.next(cx.prev_active(&dep), &cx.links);
// Alright, for each candidate that's gotten this far, it meets the
// following requirements:
cur: &mut usize,
dep: &mut Dependency,
features: &mut Rc<Vec<String>>,
- conflicting_activations: &mut HashSet<PackageId>,
+ remaining_candidates: &mut RemainingCandidates,
-) -> Option<Candidate> {
+ conflicting_activations: &mut HashMap<PackageId, ConflictReason>,
+) -> Option<(Candidate, bool)> {
while let Some(mut frame) = backtrack_stack.pop() {
- let next= frame.remaining_candidates.next(frame.context_backup.prev_active(&frame.dep));
- let (next, has_another) = {
- let prev_active = frame.context_backup.prev_active(&frame.dep);
- (
- frame.remaining_candidates.next(prev_active, &frame.context_backup.links),
- frame.remaining_candidates.clone().next(prev_active, &frame.context_backup.links).is_ok(),
- )
- };
++ let next= frame.remaining_candidates.next(frame.context_backup.prev_active(&frame.dep), &frame.context_backup.links);
if frame.context_backup.is_active(parent.package_id())
&& conflicting_activations
.iter()