From cd9bde5064257605e213f17672d74935423340cd Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 29 Nov 2023 13:41:09 -0600 Subject: [PATCH 01/10] Initial divergences from upstream (#1) --- src/internal/core.rs | 2 +- src/internal/incompatibility.rs | 4 ++-- src/internal/partial_solution.rs | 18 ++++++++++++++++++ src/lib.rs | 3 +-- src/range.rs | 21 +++++++++++++++------ src/solver.rs | 4 ++-- src/term.rs | 2 +- 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/internal/core.rs b/src/internal/core.rs index 06e3ae21..a4a23b34 100644 --- a/src/internal/core.rs +++ b/src/internal/core.rs @@ -24,7 +24,7 @@ pub struct State { root_package: P, root_version: VS::V, - incompatibilities: Map>>, + pub incompatibilities: Map>>, /// Store the ids of incompatibilities that are already contradicted /// and will stay that way until the next conflict and backtrack is operated. diff --git a/src/internal/incompatibility.rs b/src/internal/incompatibility.rs index b56a3c44..168c5218 100644 --- a/src/internal/incompatibility.rs +++ b/src/internal/incompatibility.rs @@ -31,14 +31,14 @@ use crate::version_set::VersionSet; #[derive(Debug, Clone)] pub struct Incompatibility { package_terms: SmallMap>, - kind: Kind, + pub kind: Kind, } /// Type alias of unique identifiers for incompatibilities. pub type IncompId = Id>; #[derive(Debug, Clone)] -enum Kind { +pub enum Kind { /// Initial incompatibility aiming at picking the root package for the first decision. NotRoot(P, VS::V), /// There are no versions in the given range for this package. diff --git a/src/internal/partial_solution.rs b/src/internal/partial_solution.rs index 057dea13..6a8a2bff 100644 --- a/src/internal/partial_solution.rs +++ b/src/internal/partial_solution.rs @@ -252,6 +252,24 @@ impl PartialSolution impl Iterator { + let check_all = self.changed_this_decision_level + == self.current_decision_level.0.saturating_sub(1) as usize; + let current_decision_level = self.current_decision_level; + self.package_assignments + .get_range(self.changed_this_decision_level..) + .unwrap() + .iter() + .filter(move |(_, pa)| { + // We only actually need to update the package if its Been changed + // since the last time we called prioritize. + // Which means it's highest decision level is the current decision level, + // or if we backtracked in the mean time. + check_all || pa.highest_decision_level == current_decision_level + }) + .filter_map(|(p, pa)| pa.assignments_intersection.potential_package_filter(p)) + } + pub fn pick_highest_priority_pkg( &mut self, prioritizer: impl Fn(&P, &VS) -> Priority, diff --git a/src/lib.rs b/src/lib.rs index 5f61fb51..0150c52a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -217,8 +217,7 @@ //! with a cache, you may want to know that some versions //! do not exist in your cache. -#![allow(clippy::rc_buffer)] -#![warn(missing_docs)] +#![allow(clippy::all, unreachable_pub)] pub mod error; pub mod package; diff --git a/src/range.rs b/src/range.rs index 91933e61..88a60577 100644 --- a/src/range.rs +++ b/src/range.rs @@ -196,7 +196,7 @@ impl Range { .segments .last() .expect("if there is a first element, there must be a last element"); - (start.as_ref(), end.1.as_ref()) + (bound_as_ref(start), bound_as_ref(&end.1)) }) } @@ -299,6 +299,15 @@ fn within_bounds(v: &V, segment: &Interval) -> Ordering { Ordering::Greater } +/// Implementation of [`Bound::as_ref`] which is currently marked as unstable. +fn bound_as_ref(bound: &Bound) -> Bound<&V> { + match bound { + Included(v) => Included(v), + Excluded(v) => Excluded(v), + Unbounded => Unbounded, + } +} + fn valid_segment(start: &Bound, end: &Bound) -> bool { match (start, end) { (Included(s), Included(e)) => s <= e, @@ -363,7 +372,7 @@ impl Range { (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if i <= e => Excluded(e), (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if e < i => Included(i), - (s, Unbounded) | (Unbounded, s) => s.as_ref(), + (s, Unbounded) | (Unbounded, s) => bound_as_ref(s), _ => unreachable!(), } .cloned(); @@ -373,7 +382,7 @@ impl Range { (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if i >= e => Excluded(e), (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if e > i => Included(i), - (s, Unbounded) | (Unbounded, s) => s.as_ref(), + (s, Unbounded) | (Unbounded, s) => bound_as_ref(s), _ => unreachable!(), } .cloned(); @@ -477,7 +486,7 @@ impl Display for Range { } else { for (idx, segment) in self.segments.iter().enumerate() { if idx > 0 { - write!(f, " | ")?; + write!(f, ", ")?; } match segment { (Unbounded, Unbounded) => write!(f, "*")?, @@ -486,9 +495,9 @@ impl Display for Range { (Included(v), Unbounded) => write!(f, ">={v}")?, (Included(v), Included(b)) => { if v == b { - write!(f, "{v}")? + write!(f, "=={v}")? } else { - write!(f, ">={v}, <={b}")? + write!(f, ">={v},<={b}")? } } (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?, diff --git a/src/solver.rs b/src/solver.rs index a1e5e06c..1728d1fe 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -73,8 +73,8 @@ use std::collections::{BTreeMap, BTreeSet as Set}; use std::error::Error; use crate::error::PubGrubError; -use crate::internal::core::State; -use crate::internal::incompatibility::Incompatibility; +pub use crate::internal::core::State; +pub use crate::internal::incompatibility::{Incompatibility, Kind}; use crate::package::Package; use crate::type_aliases::{DependencyConstraints, Map, SelectedDependencies}; use crate::version_set::VersionSet; diff --git a/src/term.rs b/src/term.rs index cf7aa6f7..895afdfe 100644 --- a/src/term.rs +++ b/src/term.rs @@ -64,7 +64,7 @@ impl Term { /// Unwrap the set contained in a positive term. /// Will panic if used on a negative set. - pub(crate) fn unwrap_positive(&self) -> &VS { + pub fn unwrap_positive(&self) -> &VS { match self { Self::Positive(set) => set, _ => panic!("Negative term cannot unwrap positive set"), From 1a49b7daa1c35de2885d876a588ef9c83bfdb782 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 16 Nov 2023 13:19:40 -0600 Subject: [PATCH 02/10] Add GitHub workflow to automatically tag each commit on `main` (#2) --- .github/workflows/permaref.yaml | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/permaref.yaml diff --git a/.github/workflows/permaref.yaml b/.github/workflows/permaref.yaml new file mode 100644 index 00000000..29b5dbef --- /dev/null +++ b/.github/workflows/permaref.yaml @@ -0,0 +1,40 @@ +# Automatically creates a tag for each commit to `main` so when we rebase +# changes on top of the upstream, we retain permanent references to each +# previous commit so they are not orphaned and eventually deleted. +name: Create permanent reference + +on: + push: + branches: + - "main" + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get the permanent ref number + id: get_version + run: | + # Enable pipefail so git command failures do not result in null versions downstream + set -x + + echo ::set-output name=LAST_PERMA_NUMBER::$(\ + git ls-remote --tags --refs --sort="v:refname" \ + https://github.com/zanieb/pubgrub.git | grep "tags/perma-" | tail -n1 | sed 's/.*\/perma-//' \ + ) + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Create and push the new tag + run: | + TAG="perma-$((LAST_PERMA_NUMBER + 1))" + git tag -a "$TAG" -m "Automatically created on push to `main`" + git push origin "$TAG" + env: + LAST_PERMA_NUMBER: ${{ steps.get_version.outputs.LAST_PERMA_NUMBER }} From 56ed5450e4390321a5e94765d9f9faa9c8a224d0 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 16 Nov 2023 13:36:19 -0600 Subject: [PATCH 03/10] Fix-ups to tag generating action (#3) * Use new GitHub output syntax * Fix tag message --- .github/workflows/permaref.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/permaref.yaml b/.github/workflows/permaref.yaml index 29b5dbef..eee99e6f 100644 --- a/.github/workflows/permaref.yaml +++ b/.github/workflows/permaref.yaml @@ -9,7 +9,7 @@ on: - "main" jobs: - release: + create-permaref: runs-on: ubuntu-latest steps: - name: Checkout @@ -21,10 +21,10 @@ jobs: # Enable pipefail so git command failures do not result in null versions downstream set -x - echo ::set-output name=LAST_PERMA_NUMBER::$(\ + echo "LAST_PERMA_NUMBER=$(\ git ls-remote --tags --refs --sort="v:refname" \ https://github.com/zanieb/pubgrub.git | grep "tags/perma-" | tail -n1 | sed 's/.*\/perma-//' \ - ) + )" >> $GITHUB_OUTPUT - name: Configure Git run: | @@ -34,7 +34,7 @@ jobs: - name: Create and push the new tag run: | TAG="perma-$((LAST_PERMA_NUMBER + 1))" - git tag -a "$TAG" -m "Automatically created on push to `main`" + git tag -a "$TAG" -m 'Automatically created on push to `main`' git push origin "$TAG" env: LAST_PERMA_NUMBER: ${{ steps.get_version.outputs.LAST_PERMA_NUMBER }} From d13454136967cd434691a02d0e021eb0f6aec053 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 16 Nov 2023 13:56:02 -0600 Subject: [PATCH 04/10] Add an `UnusableDependencies` incompatibility kind (#4) --- src/internal/incompatibility.rs | 16 ++++++++++++++++ src/range.rs | 4 ++++ src/report.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/src/internal/incompatibility.rs b/src/internal/incompatibility.rs index 168c5218..8de990c2 100644 --- a/src/internal/incompatibility.rs +++ b/src/internal/incompatibility.rs @@ -45,6 +45,8 @@ pub enum Kind { NoVersions(P, VS), /// Dependencies of the package are unavailable for versions in that range. UnavailableDependencies(P, VS), + /// Dependencies of the package are unusable for versions in that range. + UnusableDependencies(P, VS, Option), /// Incompatibility coming from the dependencies of a given package. FromDependencyOf(P, VS, P, VS), /// Derived from two causes. Stores cause ids. @@ -104,6 +106,17 @@ impl Incompatibility { } } + /// Create an incompatibility to remember + /// that a package version is not selectable + /// because its dependencies are not usable. + pub fn unusable_dependencies(package: P, version: VS::V, reason: Option) -> Self { + let set = VS::singleton(version); + Self { + package_terms: SmallMap::One([(package.clone(), Term::Positive(set.clone()))]), + kind: Kind::UnusableDependencies(package, set, reason), + } + } + /// Build an incompatibility from a given dependency. pub fn from_dependency(package: P, version: VS::V, dep: (&P, &VS)) -> Self { let set1 = VS::singleton(version); @@ -206,6 +219,9 @@ impl Incompatibility { Kind::UnavailableDependencies(package, set) => DerivationTree::External( External::UnavailableDependencies(package.clone(), set.clone()), ), + Kind::UnusableDependencies(package, set, reason) => DerivationTree::External( + External::UnusableDependencies(package.clone(), set.clone(), reason.clone()), + ), Kind::FromDependencyOf(package, set, dep_package, dep_set) => { DerivationTree::External(External::FromDependencyOf( package.clone(), diff --git a/src/range.rs b/src/range.rs index 88a60577..dfc8be33 100644 --- a/src/range.rs +++ b/src/range.rs @@ -118,6 +118,10 @@ impl Range { segments: SmallVec::one((Included(v1.into()), Excluded(v2.into()))), } } + + pub fn is_empty(&self) -> bool { + self.segments.is_empty() + } } impl Range { diff --git a/src/report.rs b/src/report.rs index ff0b2d3f..57e862bd 100644 --- a/src/report.rs +++ b/src/report.rs @@ -41,6 +41,8 @@ pub enum External { NoVersions(P, VS), /// Dependencies of the package are unavailable for versions in that set. UnavailableDependencies(P, VS), + /// Dependencies of the package are unusable for versions in that set. + UnusableDependencies(P, VS, Option), /// Incompatibility coming from the dependencies of a given package. FromDependencyOf(P, VS, P, VS), } @@ -113,6 +115,13 @@ impl DerivationTree { DerivationTree::External(External::UnavailableDependencies(_, r)) => Some( DerivationTree::External(External::UnavailableDependencies(package, set.union(&r))), ), + DerivationTree::External(External::UnusableDependencies(_, r, reason)) => { + Some(DerivationTree::External(External::UnusableDependencies( + package, + set.union(&r), + reason, + ))) + } DerivationTree::External(External::FromDependencyOf(p1, r1, p2, r2)) => { if p1 == package { Some(DerivationTree::External(External::FromDependencyOf( @@ -158,6 +167,29 @@ impl fmt::Display for External { ) } } + Self::UnusableDependencies(package, set, reason) => { + if let Some(reason) = reason { + if set == &VS::full() { + write!(f, "dependencies of {} are unusable: {reason}", package) + } else { + write!( + f, + "dependencies of {} at version {} are unusable: {reason}", + package, set + ) + } + } else { + if set == &VS::full() { + write!(f, "dependencies of {} are unusable", package) + } else { + write!( + f, + "dependencies of {} at version {} are unusable", + package, set + ) + } + } + } Self::FromDependencyOf(p, set_p, dep, set_dep) => { if set_p == &VS::full() && set_dep == &VS::full() { write!(f, "{} depends on {}", p, dep) From 07c72c22c0382a71c48ae6209a7fdb6684fec0bf Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 29 Nov 2023 13:41:22 -0600 Subject: [PATCH 05/10] Update `Range` to match upstream (#5) --- src/range.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/range.rs b/src/range.rs index dfc8be33..b17861b9 100644 --- a/src/range.rs +++ b/src/range.rs @@ -200,7 +200,7 @@ impl Range { .segments .last() .expect("if there is a first element, there must be a last element"); - (bound_as_ref(start), bound_as_ref(&end.1)) + (start.as_ref(), end.1.as_ref()) }) } @@ -303,15 +303,6 @@ fn within_bounds(v: &V, segment: &Interval) -> Ordering { Ordering::Greater } -/// Implementation of [`Bound::as_ref`] which is currently marked as unstable. -fn bound_as_ref(bound: &Bound) -> Bound<&V> { - match bound { - Included(v) => Included(v), - Excluded(v) => Excluded(v), - Unbounded => Unbounded, - } -} - fn valid_segment(start: &Bound, end: &Bound) -> bool { match (start, end) { (Included(s), Included(e)) => s <= e, @@ -376,7 +367,7 @@ impl Range { (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if i <= e => Excluded(e), (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if e < i => Included(i), - (s, Unbounded) | (Unbounded, s) => bound_as_ref(s), + (s, Unbounded) | (Unbounded, s) => s.as_ref(), _ => unreachable!(), } .cloned(); @@ -386,7 +377,7 @@ impl Range { (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if i >= e => Excluded(e), (Included(i), Excluded(e)) | (Excluded(e), Included(i)) if e > i => Included(i), - (s, Unbounded) | (Unbounded, s) => bound_as_ref(s), + (s, Unbounded) | (Unbounded, s) => s.as_ref(), _ => unreachable!(), } .cloned(); @@ -490,7 +481,7 @@ impl Display for Range { } else { for (idx, segment) in self.segments.iter().enumerate() { if idx > 0 { - write!(f, ", ")?; + write!(f, " | ")?; } match segment { (Unbounded, Unbounded) => write!(f, "*")?, @@ -501,7 +492,7 @@ impl Display for Range { if v == b { write!(f, "=={v}")? } else { - write!(f, ">={v},<={b}")? + write!(f, ">={v}, <={b}")? } } (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?, From 625007411262ea4e4ce70653a117f3a2928d7242 Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 29 Nov 2023 13:42:21 -0600 Subject: [PATCH 06/10] Add `Reporter.simplify_versions` --- examples/holes.rs | 44 ++++++++++++++++++++++++++++++++++ src/range.rs | 60 +++++++++++++++++++++++----------------------- src/report.rs | 39 ++++++++++++++++++++++++++++++ src/version_set.rs | 5 ++++ 4 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 examples/holes.rs diff --git a/examples/holes.rs b/examples/holes.rs new file mode 100644 index 00000000..aa7e8633 --- /dev/null +++ b/examples/holes.rs @@ -0,0 +1,44 @@ +use pubgrub::error::PubGrubError; +use pubgrub::range::Range; +use pubgrub::report::{DefaultStringReporter, Reporter}; +use pubgrub::solver::{resolve, OfflineDependencyProvider}; +use pubgrub::version::SemanticVersion; +use rustc_hash::FxHashMap; + +fn main() { + let mut dependency_provider = OfflineDependencyProvider::<&str, Range>::new(); + + // root depends on foo... + dependency_provider.add_dependencies("root", (1, 0, 0), vec![("foo", Range::full())]); + + for i in 1..5 { + // foo depends on bar... + dependency_provider.add_dependencies("foo", (i, 0, 0), vec![("bar", Range::full())]); + } + + let mut versions = FxHashMap::default(); + for package in dependency_provider.packages() { + versions.insert( + *package, + dependency_provider + .versions(package) + .expect("Package must have versions") + .cloned() + .collect::>(), + ); + } + + match resolve(&dependency_provider, "root", (1, 0, 0)) { + Ok(sol) => println!("{:?}", sol), + Err(PubGrubError::NoSolution(derivation_tree)) => { + let simple_deriviation_tree = derivation_tree.simplify_versions(&versions); + eprintln!("{}", DefaultStringReporter::report(&derivation_tree)); + eprintln!("\n----------\n"); + eprintln!( + "{}", + DefaultStringReporter::report(&simple_deriviation_tree) + ); + } + Err(err) => panic!("{:?}", err), + }; +} diff --git a/src/range.rs b/src/range.rs index b17861b9..0feb4bff 100644 --- a/src/range.rs +++ b/src/range.rs @@ -391,36 +391,6 @@ impl Range { Self { segments }.check_invariants() } - /// Returns a simpler Range that contains the same versions - /// - /// For every one of the Versions provided in versions the existing range and - /// the simplified range will agree on whether it is contained. - /// The simplified version may include or exclude versions that are not in versions as the implementation wishes. - /// For example: - /// - If all the versions are contained in the original than the range will be simplified to `full`. - /// - If none of the versions are contained in the original than the range will be simplified to `empty`. - /// - /// If versions are not sorted the correctness of this function is not guaranteed. - pub fn simplify<'v, I>(&self, versions: I) -> Self - where - I: Iterator + 'v, - V: 'v, - { - // Return the segment index in the range for each version in the range, None otherwise - let version_locations = versions.scan(0, move |i, v| { - while let Some(segment) = self.segments.get(*i) { - match within_bounds(v, segment) { - Ordering::Less => return Some(None), - Ordering::Equal => return Some(Some(*i)), - Ordering::Greater => *i += 1, - } - } - Some(None) - }); - let kept_segments = group_adjacent_locations(version_locations); - self.keep_segments(kept_segments) - } - /// Create a new range with a subset of segments at given location bounds. /// /// Each new segment is constructed from a pair of segments, taking the @@ -470,6 +440,36 @@ impl VersionSet for Range { fn union(&self, other: &Self) -> Self { Range::union(self, other) } + + /// Returns a simpler Range that contains the same versions + /// + /// For every one of the Versions provided in versions the existing range and + /// the simplified range will agree on whether it is contained. + /// The simplified version may include or exclude versions that are not in versions as the implementation wishes. + /// For example: + /// - If all the versions are contained in the original than the range will be simplified to `full`. + /// - If none of the versions are contained in the original than the range will be simplified to `empty`. + /// + /// If versions are not sorted the correctness of this function is not guaranteed. + fn simplify<'s, I>(&self, versions: I) -> Self + where + I: Iterator + 's, + Self::V: 's, + { + // Return the segment index in the range for each version in the range, None otherwise + let version_locations = versions.scan(0, move |i, v| { + while let Some(segment) = self.segments.get(*i) { + match within_bounds(v, segment) { + Ordering::Less => return Some(None), + Ordering::Equal => return Some(Some(*i)), + Ordering::Greater => *i += 1, + } + } + Some(None) + }); + let kept_segments = group_adjacent_locations(version_locations); + self.keep_segments(kept_segments) + } } // REPORT ###################################################################### diff --git a/src/report.rs b/src/report.rs index 57e862bd..a702e18f 100644 --- a/src/report.rs +++ b/src/report.rs @@ -6,6 +6,8 @@ use std::fmt; use std::ops::{Deref, DerefMut}; +use rustc_hash::FxHashMap; + use crate::package::Package; use crate::term::Term; use crate::type_aliases::Map; @@ -141,6 +143,43 @@ impl DerivationTree { } } } + + /// Simplify version ranges in the deriviation tree using the available + /// versions for each package. + pub fn simplify_versions(&self, versions: &FxHashMap>) -> Self { + match self { + DerivationTree::External(external) => { + let external = match external { + External::NotRoot(..) => external.clone(), + External::FromDependencyOf(l_p, l_vs, r_p, r_vs) => External::FromDependencyOf( + l_p.clone(), + l_vs.clone() + .simplify(versions.get(&l_p).unwrap_or(&Vec::new()).into_iter()), + r_p.clone(), + r_vs.clone() + .simplify(versions.get(&r_p).unwrap_or(&Vec::new()).into_iter()), + ), + External::NoVersions(p, vs) => External::NoVersions( + p.clone(), + vs.clone() + .simplify(versions.get(&p).unwrap_or(&Vec::new()).into_iter()), + ), + External::UnavailableDependencies(p, vs) => External::UnavailableDependencies( + p.clone(), + vs.clone() + .simplify(versions.get(&p).unwrap_or(&Vec::new()).into_iter()), + ), + }; + DerivationTree::External(external) + } + DerivationTree::Derived(derived) => DerivationTree::Derived(Derived { + terms: derived.terms.clone(), + shared_id: derived.shared_id, + cause1: Box::new(derived.cause1.simplify_versions(versions)), + cause2: Box::new(derived.cause2.simplify_versions(versions)), + }), + } + } } impl fmt::Display for External { diff --git a/src/version_set.rs b/src/version_set.rs index 501ec700..a38921c6 100644 --- a/src/version_set.rs +++ b/src/version_set.rs @@ -37,6 +37,11 @@ pub trait VersionSet: Debug + Display + Clone + Eq { /// Compute the intersection with another set. fn intersection(&self, other: &Self) -> Self; + fn simplify<'s, I>(&'s self, versions: I) -> Self + where + I: Iterator + 's, + Self::V: 's; + // Membership /// Evaluate membership of a version in this set. fn contains(&self, v: &Self::V) -> bool; From 36573f152ca819ac92d1ebd9d60db603c93873b8 Mon Sep 17 00:00:00 2001 From: Zanie Date: Tue, 28 Nov 2023 13:38:08 -0600 Subject: [PATCH 07/10] Simplify terms --- src/report.rs | 12 +++++++++++- src/term.rs | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/report.rs b/src/report.rs index a702e18f..01639007 100644 --- a/src/report.rs +++ b/src/report.rs @@ -173,7 +173,17 @@ impl DerivationTree { DerivationTree::External(external) } DerivationTree::Derived(derived) => DerivationTree::Derived(Derived { - terms: derived.terms.clone(), + terms: derived + .terms + .iter() + .map(|(p, t)| { + ( + p.clone(), + t.simplify(versions.get(&p).unwrap_or(&Vec::new()).into_iter()), + ) + }) + .collect(), + shared_id: derived.shared_id, cause1: Box::new(derived.cause1.simplify_versions(versions)), cause2: Box::new(derived.cause2.simplify_versions(versions)), diff --git a/src/term.rs b/src/term.rs index 895afdfe..62de20bb 100644 --- a/src/term.rs +++ b/src/term.rs @@ -101,6 +101,17 @@ impl Term { pub(crate) fn subset_of(&self, other: &Self) -> bool { self == &self.intersection(other) } + + pub(crate) fn simplify<'s, I>(&'s self, versions: I) -> Self + where + I: Iterator + 's, + VS::V: 's, + { + match self { + Self::Positive(set) => Self::Positive(set.simplify(versions)), + Self::Negative(set) => Self::Negative(set.simplify(versions)), + } + } } /// Describe a relation between a set of terms S and another term t. From 438062ffacdafa2b8f0c4c3eb6220ed40152538c Mon Sep 17 00:00:00 2001 From: Zanie Date: Tue, 28 Nov 2023 13:42:03 -0600 Subject: [PATCH 08/10] Improve handling of empty sets in reporter --- src/report.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/report.rs b/src/report.rs index 01639007..8761a14d 100644 --- a/src/report.rs +++ b/src/report.rs @@ -199,14 +199,14 @@ impl fmt::Display for External { write!(f, "we are solving dependencies of {} {}", package, version) } Self::NoVersions(package, set) => { - if set == &VS::full() { + if set == &VS::full() || set == &VS::empty() { write!(f, "there is no available version for {}", package) } else { write!(f, "there is no version of {} in {}", package, set) } } Self::UnavailableDependencies(package, set) => { - if set == &VS::full() { + if set == &VS::full() || set == &VS::empty() { write!(f, "dependencies of {} are unavailable", package) } else { write!( @@ -240,11 +240,13 @@ impl fmt::Display for External { } } Self::FromDependencyOf(p, set_p, dep, set_dep) => { - if set_p == &VS::full() && set_dep == &VS::full() { + if (set_p == &VS::full() || set_p == &VS::empty()) + && (set_dep == &VS::full() || set_dep == &VS::empty()) + { write!(f, "{} depends on {}", p, dep) - } else if set_p == &VS::full() { + } else if set_p == &VS::full() || set_p == &VS::empty() { write!(f, "{} depends on {} {}", p, dep, set_dep) - } else if set_dep == &VS::full() { + } else if set_dep == &VS::full() || set_dep == &VS::empty() { write!(f, "{} {} depends on {}", p, set_p, dep) } else { write!(f, "{} {} depends on {} {}", p, set_p, dep, set_dep) From d14bf2b1181cd3dd468d50f0f79fe273a9a8f8ef Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 29 Nov 2023 13:47:06 -0600 Subject: [PATCH 09/10] Add docs to trait method --- src/version_set.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/version_set.rs b/src/version_set.rs index a38921c6..2523b277 100644 --- a/src/version_set.rs +++ b/src/version_set.rs @@ -37,6 +37,7 @@ pub trait VersionSet: Debug + Display + Clone + Eq { /// Compute the intersection with another set. fn intersection(&self, other: &Self) -> Self; + /// Return a simplified version set, collapsing redundancies. fn simplify<'s, I>(&'s self, versions: I) -> Self where I: Iterator + 's, From cc055ab32da3a6639c02f1c62a381523ec69ae98 Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 29 Nov 2023 15:20:55 -0600 Subject: [PATCH 10/10] Add `UnusableDependencies` case --- src/report.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/report.rs b/src/report.rs index 8761a14d..2e611a2a 100644 --- a/src/report.rs +++ b/src/report.rs @@ -169,6 +169,14 @@ impl DerivationTree { vs.clone() .simplify(versions.get(&p).unwrap_or(&Vec::new()).into_iter()), ), + External::UnusableDependencies(p, vs, reason) => { + External::UnusableDependencies( + p.clone(), + vs.clone() + .simplify(versions.get(&p).unwrap_or(&Vec::new()).into_iter()), + reason.clone(), + ) + } }; DerivationTree::External(external) }