diff --git a/Cargo.lock b/Cargo.lock index 8425829..4f428e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1107,6 +1107,7 @@ dependencies = [ "tokio", "tracing", "tracing-test", + "version-ranges", ] [[package]] @@ -1476,6 +1477,15 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +[[package]] +name = "version-ranges" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284649eba55872c1253f3f6ec15f22303a784e60684babd01d01e4c6ebb85b91" +dependencies = [ + "smallvec", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index bc674e1..f6727a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,11 +39,12 @@ event-listener = "5.3" indexmap = "2" tokio = { version = "1.37", features = ["rt"], optional = true } async-std = { version = "1.12", default-features = false, features = ["alloc", "default"], optional = true } +version-ranges = { version = "0.1.0", optional = true } [dev-dependencies] insta = "1.39.0" proptest = "1.2" tracing-test = { version = "0.2.4", features = ["no-env-filter"] } tokio = { version = "1.35.1", features = ["time", "rt"] } -resolvo = { path = ".", features = ["tokio"] } +resolvo = { path = ".", features = ["tokio", "version-ranges"] } serde_json = "1.0" diff --git a/src/conflict.rs b/src/conflict.rs index 4a7276b..edad5e9 100644 --- a/src/conflict.rs +++ b/src/conflict.rs @@ -11,9 +11,11 @@ use petgraph::{ Direction, }; -use crate::internal::arena::ArenaId; use crate::{ - internal::id::{ClauseId, InternalSolvableId, SolvableId, StringId, VersionSetId}, + internal::{ + arena::ArenaId, + id::{ClauseId, InternalSolvableId, SolvableId, StringId, VersionSetId}, + }, runtime::AsyncRuntime, solver::{clause::Clause, Solver}, DependencyProvider, Interner, Requirement, @@ -908,7 +910,7 @@ impl<'i, I: Interner> DisplayUnsat<'i, I> { let indent = indenter.get_indent(); writeln!( f, - "{indent}{name} {version_set} , which conflicts with any installable versions previously reported", + "{indent}{name} {version_set}, which conflicts with any installable versions previously reported", )?; } } else { @@ -985,7 +987,10 @@ impl<'i, I: Interner> fmt::Display for DisplayUnsat<'i, I> { &ConflictCause::Constrains(version_set_id) => { writeln!( f, - "{indent}constraint '{version_set}' cannot be fulfilled", + "{indent}the constraint {name} {version_set} cannot be fulfilled", + name = self + .interner + .display_name(self.interner.version_set_name(version_set_id)), version_set = self.interner.display_version_set(version_set_id), )?; } diff --git a/src/snapshot.rs b/src/snapshot.rs index fcef386..0b8b6d2 100644 --- a/src/snapshot.rs +++ b/src/snapshot.rs @@ -370,11 +370,7 @@ impl<'s> SnapshotProvider<'s> { self.additional_version_sets.push(VersionSet { name, - display: if matcher == "*" { - "*".to_string() - } else { - format!("{} {}", package.name, matcher) - }, + display: matcher.to_string(), matching_candidates, }); diff --git a/src/solver/cache.rs b/src/solver/cache.rs index e66a950..0a35b5a 100644 --- a/src/solver/cache.rs +++ b/src/solver/cache.rs @@ -242,8 +242,9 @@ impl SolverCache { } } - /// Returns the candidates fulfilling the [`Requirement`] sorted from highest to lowest - /// within each version set comprising the [`Requirement`]. + /// Returns the candidates fulfilling the [`Requirement`] sorted from + /// highest to lowest within each version set comprising the + /// [`Requirement`]. /// /// If the provider has requested the solving process to be cancelled, the /// cancellation value will be returned as an `Err(...)`. diff --git a/src/solver/mod.rs b/src/solver/mod.rs index 2083460..c01997d 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -958,10 +958,6 @@ impl Solver { self.negative_assertions .append(&mut output.negative_assertions); - if let Some(&clause_id) = output.conflicting_clauses.first() { - return Err(clause_id); - } - if let Some(max_name_idx) = output .new_names .into_iter() @@ -973,6 +969,10 @@ impl Solver { } } + if let Some(&clause_id) = output.conflicting_clauses.first() { + return Err(clause_id); + } + Ok(()) } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index d9fa8e7..6f2bada 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,7 +2,5 @@ //! implement a custom dependency provider. mod pool; -mod range; pub use pool::{PackageName, Pool, VersionSet}; -pub use range::Range; diff --git a/src/utils/pool.rs b/src/utils/pool.rs index 5fffddd..2a3b6fe 100644 --- a/src/utils/pool.rs +++ b/src/utils/pool.rs @@ -187,10 +187,11 @@ impl Pool { self.version_sets[id].0 } - /// Interns a union of two or more version sets and returns its [`VersionSetUnionId`]. + /// Interns a union of two or more version sets and returns its + /// [`VersionSetUnionId`]. /// - /// Version set unions are *not* deduplicated, and a unique id is returned on every - /// invocation. + /// Version set unions are *not* deduplicated, and a unique id is returned + /// on every invocation. pub fn intern_version_set_union( &self, first: VersionSetId, @@ -261,3 +262,8 @@ pub trait VersionSet: Clone + Eq + Hash { /// The element type that is included in the set. type V: Display; } + +#[cfg(feature = "version-ranges")] +impl VersionSet for version_ranges::Ranges { + type V = R; +} diff --git a/src/utils/range.rs b/src/utils/range.rs deleted file mode 100644 index 8b35705..0000000 --- a/src/utils/range.rs +++ /dev/null @@ -1,522 +0,0 @@ -// This file was originally taken from: -// -// SPDX-License-Identifier: MPL-2.0 - -//! Ranges are constraints defining sets of versions. They are a convenience struct that implements -//! [`crate::pool::VersionSet`] for any version type that implements [`Ord`] + [`Clone`]. -//! -//! Concretely, those constraints correspond to any set of versions representable as the -//! concatenation, union, and complement of the ranges building blocks. -//! -//! Those building blocks are: -//! - [empty()](Range::empty): the empty set -//! - [full()](Range::full): the set of all possible versions -//! - [singleton(v)](Range::singleton): the set containing only the version v -//! - [higher_than(v)](Range::higher_than): the set defined by `v <= versions` -//! - [strictly_higher_than(v)](Range::strictly_higher_than): the set defined by `v < versions` -//! - [lower_than(v)](Range::lower_than): the set defined by `versions <= v` -//! - [strictly_lower_than(v)](Range::strictly_lower_than): the set defined by `versions < v` -//! - [between(v1, v2)](Range::between): the set defined by `v1 <= versions < v2` -//! -//! Ranges can be created from any type that implements [`Ord`] + [`Clone`]. - -use super::pool::VersionSet; -use crate::internal::small_vec::SmallVec; -use std::{ - fmt::{Debug, Display, Formatter}, - hash::Hash, - ops::Bound::{self, Excluded, Included, Unbounded}, - ops::RangeBounds, -}; - -/// A Range represents multiple intervals of a continuous range of monotone increasing -/// values. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(transparent))] -pub struct Range { - segments: SmallVec>, -} - -type Interval = (Bound, Bound); - -impl Range { - /// Empty set of versions. - pub fn empty() -> Self { - Self { - segments: SmallVec::empty(), - } - } - - /// Set of all possible versions - pub fn full() -> Self { - Self { - segments: SmallVec::one((Unbounded, Unbounded)), - } - } - - /// Set of all versions higher or equal to some version - pub fn higher_than(v: impl Into) -> Self { - Self { - segments: SmallVec::one((Included(v.into()), Unbounded)), - } - } - - /// Set of all versions higher to some version - pub fn strictly_higher_than(v: impl Into) -> Self { - Self { - segments: SmallVec::one((Excluded(v.into()), Unbounded)), - } - } - - /// Set of all versions lower to some version - pub fn strictly_lower_than(v: impl Into) -> Self { - Self { - segments: SmallVec::one((Unbounded, Excluded(v.into()))), - } - } - - /// Set of all versions lower or equal to some version - pub fn lower_than(v: impl Into) -> Self { - Self { - segments: SmallVec::one((Unbounded, Included(v.into()))), - } - } - - /// Set of versions greater or equal to `v1` but less than `v2`. - pub fn between(v1: impl Into, v2: impl Into) -> Self { - Self { - segments: SmallVec::one((Included(v1.into()), Excluded(v2.into()))), - } - } -} - -impl Range { - /// Set containing exactly one version - pub fn singleton(v: impl Into) -> Self { - let v = v.into(); - Self { - segments: SmallVec::one((Included(v.clone()), Included(v))), - } - } - - /// Returns the complement of this Range. - pub fn complement(&self) -> Self { - match self.segments.first() { - // Complement of ∅ is ∞ - None => Self::full(), - - // Complement of ∞ is ∅ - Some((Unbounded, Unbounded)) => Self::empty(), - - // First high bound is +∞ - Some((Included(v), Unbounded)) => Self::strictly_lower_than(v.clone()), - Some((Excluded(v), Unbounded)) => Self::lower_than(v.clone()), - - Some((Unbounded, Included(v))) => { - Self::negate_segments(Excluded(v.clone()), &self.segments[1..]) - } - Some((Unbounded, Excluded(v))) => { - Self::negate_segments(Included(v.clone()), &self.segments[1..]) - } - Some((Included(_), Included(_))) - | Some((Included(_), Excluded(_))) - | Some((Excluded(_), Included(_))) - | Some((Excluded(_), Excluded(_))) => Self::negate_segments(Unbounded, &self.segments), - } - } - - /// Helper function performing the negation of intervals in segments. - fn negate_segments(start: Bound, segments: &[Interval]) -> Self { - let mut complement_segments: SmallVec> = SmallVec::empty(); - let mut start = start; - for (v1, v2) in segments { - complement_segments.push(( - start, - match v1 { - Included(v) => Excluded(v.clone()), - Excluded(v) => Included(v.clone()), - Unbounded => unreachable!(), - }, - )); - start = match v2 { - Included(v) => Excluded(v.clone()), - Excluded(v) => Included(v.clone()), - Unbounded => Unbounded, - } - } - if !matches!(start, Unbounded) { - complement_segments.push((start, Unbounded)); - } - - Self { - segments: complement_segments, - } - } -} - -impl Range { - /// Convert to something that can be used with - /// [BTreeMap::range](std::collections::BTreeMap::range). - /// All versions contained in self, will be in the output, - /// but there may be versions in the output that are not contained in self. - /// Returns None if the range is empty. - pub fn bounding_range(&self) -> Option<(Bound<&V>, Bound<&V>)> { - self.segments.first().map(|(start, _)| { - let end = self - .segments - .last() - .expect("if there is a first element, there must be a last element"); - (bound_as_ref(start), bound_as_ref(&end.1)) - }) - } - - /// Returns true if the this Range contains the specified value. - pub fn contains(&self, v: &V) -> bool { - for segment in self.segments.iter() { - if match segment { - (Unbounded, Unbounded) => true, - (Unbounded, Included(end)) => v <= end, - (Unbounded, Excluded(end)) => v < end, - (Included(start), Unbounded) => v >= start, - (Included(start), Included(end)) => v >= start && v <= end, - (Included(start), Excluded(end)) => v >= start && v < end, - (Excluded(start), Unbounded) => v > start, - (Excluded(start), Included(end)) => v > start && v <= end, - (Excluded(start), Excluded(end)) => v > start && v < end, - } { - return true; - } - } - false - } - - /// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`. - pub fn from_range_bounds(bounds: R) -> Self - where - R: RangeBounds, - IV: Clone + Into, - { - let start = match bounds.start_bound() { - Included(v) => Included(v.clone().into()), - Excluded(v) => Excluded(v.clone().into()), - Unbounded => Unbounded, - }; - let end = match bounds.end_bound() { - Included(v) => Included(v.clone().into()), - Excluded(v) => Excluded(v.clone().into()), - Unbounded => Unbounded, - }; - if valid_segment(&start, &end) { - Self { - segments: SmallVec::one((start, end)), - } - } else { - Self::empty() - } - } - - fn check_invariants(self) -> Self { - if cfg!(debug_assertions) { - for p in self.segments.as_slice().windows(2) { - match (&p[0].1, &p[1].0) { - (Included(l_end), Included(r_start)) => assert!(l_end < r_start), - (Included(l_end), Excluded(r_start)) => assert!(l_end < r_start), - (Excluded(l_end), Included(r_start)) => assert!(l_end < r_start), - (Excluded(l_end), Excluded(r_start)) => assert!(l_end <= r_start), - (_, Unbounded) => panic!(), - (Unbounded, _) => panic!(), - } - } - for (s, e) in self.segments.iter() { - assert!(valid_segment(s, e)); - } - } - self - } -} - -/// 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, - (Included(s), Excluded(e)) => s < e, - (Excluded(s), Included(e)) => s < e, - (Excluded(s), Excluded(e)) => s < e, - (Unbounded, _) | (_, Unbounded) => true, - } -} - -impl Range { - /// Computes the union of this `Range` and another. - pub fn union(&self, other: &Self) -> Self { - self.complement() - .intersection(&other.complement()) - .complement() - .check_invariants() - } - - /// Computes the intersection of two sets of versions. - pub fn intersection(&self, other: &Self) -> Self { - let mut segments: SmallVec> = SmallVec::empty(); - let mut left_iter = self.segments.iter().peekable(); - let mut right_iter = other.segments.iter().peekable(); - - while let (Some((left_start, left_end)), Some((right_start, right_end))) = - (left_iter.peek(), right_iter.peek()) - { - let start = match (left_start, right_start) { - (Included(l), Included(r)) => Included(std::cmp::max(l, r)), - (Excluded(l), Excluded(r)) => Excluded(std::cmp::max(l, r)), - - (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), - _ => unreachable!(), - } - .cloned(); - let end = match (left_end, right_end) { - (Included(l), Included(r)) => Included(std::cmp::min(l, r)), - (Excluded(l), Excluded(r)) => Excluded(std::cmp::min(l, r)), - - (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), - _ => unreachable!(), - } - .cloned(); - left_iter.next_if(|(_, e)| e == &end); - right_iter.next_if(|(_, e)| e == &end); - if valid_segment(&start, &end) { - segments.push((start, end)) - } - } - - Self { segments }.check_invariants() - } -} - -impl VersionSet for Range { - type V = T; -} - -impl Display for Range { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if self.segments.is_empty() { - write!(f, "∅")?; - } else { - for (idx, segment) in self.segments.iter().enumerate() { - if idx > 0 { - write!(f, ", ")?; - } - match segment { - (Unbounded, Unbounded) => write!(f, "*")?, - (Unbounded, Included(v)) => write!(f, "<={v}")?, - (Unbounded, Excluded(v)) => write!(f, "<{v}")?, - (Included(v), Unbounded) => write!(f, ">={v}")?, - (Included(v), Included(b)) => { - if v == b { - write!(f, "{v}")? - } else { - write!(f, ">={v},<={b}")? - } - } - (Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?, - (Excluded(v), Unbounded) => write!(f, ">{v}")?, - (Excluded(v), Included(b)) => write!(f, ">{v}, <={b}")?, - (Excluded(v), Excluded(b)) => write!(f, ">{v}, <{b}")?, - }; - } - } - Ok(()) - } -} - -#[cfg(test)] -pub mod tests { - use proptest::prelude::*; - - use super::*; - - /// Generate version sets from a random vector of deltas between bounds. - /// Each bound is randomly inclusive or exclusive. - pub fn strategy() -> impl Strategy> { - ( - any::(), - prop::collection::vec(any::<(u32, bool)>(), 1..10), - ) - .prop_map(|(start_unbounded, deltas)| { - let mut start = if start_unbounded { - Some(Unbounded) - } else { - None - }; - let mut largest: u32 = 0; - let mut last_bound_was_inclusive = false; - let mut segments = SmallVec::Empty; - for (delta, inclusive) in deltas { - // Add the offset to the current bound - largest = match largest.checked_add(delta) { - Some(s) => s, - None => { - // Skip this offset, if it would result in a too large bound. - continue; - } - }; - - let current_bound = if inclusive { - Included(largest) - } else { - Excluded(largest) - }; - - // If we already have a start bound, the next offset defines the complete range. - // If we don't have a start bound, we have to generate one. - if let Some(start_bound) = start.take() { - // If the delta from the start bound is 0, the only authorized configuration is - // Included(x), Included(x) - if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) { - start = Some(start_bound); - continue; - } - last_bound_was_inclusive = inclusive; - segments.push((start_bound, current_bound)); - } else { - // If the delta from the end bound of the last range is 0 and - // any of the last ending or current starting bound is inclusive, - // we skip the delta because they basically overlap. - if delta == 0 && (last_bound_was_inclusive || inclusive) { - continue; - } - start = Some(current_bound); - } - } - - // If we still have a start bound, but didn't have enough deltas to complete another - // segment, we add an unbounded upperbound. - if let Some(start_bound) = start { - segments.push((start_bound, Unbounded)); - } - - Range { segments }.check_invariants() - }) - } - - fn version_strat() -> impl Strategy { - any::() - } - - proptest! { - - // Testing negate ---------------------------------- - - #[test] - fn negate_is_different(range in strategy()) { - assert_ne!(range.complement(), range); - } - - #[test] - fn double_negate_is_identity(range in strategy()) { - assert_eq!(range.complement().complement(), range); - } - - #[test] - fn negate_contains_opposite(range in strategy(), version in version_strat()) { - assert_ne!(range.contains(&version), range.complement().contains(&version)); - } - - // Testing intersection ---------------------------- - - #[test] - fn intersection_is_symmetric(r1 in strategy(), r2 in strategy()) { - assert_eq!(r1.intersection(&r2), r2.intersection(&r1)); - } - - #[test] - fn intersection_with_any_is_identity(range in strategy()) { - assert_eq!(Range::full().intersection(&range), range); - } - - #[test] - fn intersection_with_none_is_none(range in strategy()) { - assert_eq!(Range::empty().intersection(&range), Range::empty()); - } - - #[test] - fn intersection_is_idempotent(r1 in strategy(), r2 in strategy()) { - assert_eq!(r1.intersection(&r2).intersection(&r2), r1.intersection(&r2)); - } - - #[test] - fn intersection_is_associative(r1 in strategy(), r2 in strategy(), r3 in strategy()) { - assert_eq!(r1.intersection(&r2).intersection(&r3), r1.intersection(&r2.intersection(&r3))); - } - - #[test] - fn intesection_of_complements_is_none(range in strategy()) { - assert_eq!(range.complement().intersection(&range), Range::empty()); - } - - #[test] - fn intesection_contains_both(r1 in strategy(), r2 in strategy(), version in version_strat()) { - assert_eq!(r1.intersection(&r2).contains(&version), r1.contains(&version) && r2.contains(&version)); - } - - // Testing union ----------------------------------- - - #[test] - fn union_of_complements_is_any(range in strategy()) { - assert_eq!(range.complement().union(&range), Range::full()); - } - - #[test] - fn union_contains_either(r1 in strategy(), r2 in strategy(), version in version_strat()) { - assert_eq!(r1.union(&r2).contains(&version), r1.contains(&version) || r2.contains(&version)); - } - - // Testing contains -------------------------------- - - #[test] - fn always_contains_exact(version in version_strat()) { - assert!(Range::singleton(version).contains(&version)); - } - - #[test] - fn contains_negation(range in strategy(), version in version_strat()) { - assert_ne!(range.contains(&version), range.complement().contains(&version)); - } - - #[test] - fn contains_intersection(range in strategy(), version in version_strat()) { - assert_eq!(range.contains(&version), range.intersection(&Range::singleton(version)) != Range::empty()); - } - - #[test] - fn contains_bounding_range(range in strategy(), version in version_strat()) { - if range.contains(&version) { - assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false)); - } - } - - #[test] - fn from_range_bounds(range in any::<(Bound, Bound)>(), version in version_strat()) { - let rv: Range<_> = Range::from_range_bounds(range); - assert_eq!(range.contains(&version), rv.contains(&version)); - } - - #[test] - fn from_range_bounds_round_trip(range in any::<(Bound, Bound)>()) { - let rv: Range = Range::from_range_bounds(range); - let rv2: Range = rv.bounding_range().map(Range::from_range_bounds::<_, u32>).unwrap_or_else(Range::empty); - assert_eq!(rv, rv2); - } - } -} diff --git a/tests/snapshots/solver__root_constraints.snap b/tests/snapshots/solver__root_constraints.snap index f498bff..160d78c 100644 --- a/tests/snapshots/solver__root_constraints.snap +++ b/tests/snapshots/solver__root_constraints.snap @@ -1,8 +1,8 @@ --- source: tests/solver.rs -expression: "solve_for_snapshot(snapshot_provider, &[union_req], &[icons_req])" +expression: "solve_for_snapshot(snapshot_provider, &[union_req], &[union_constraint])" --- The following packages are incompatible └─ union * can be installed with any of the following options: └─ union union=1 -├─ constraint 'union 5' cannot be fulfilled +├─ the constraint union 5 cannot be fulfilled diff --git a/tests/snapshots/solver__unsat_constrains.snap b/tests/snapshots/solver__unsat_constrains.snap index 9799353..057b37f 100644 --- a/tests/snapshots/solver__unsat_constrains.snap +++ b/tests/snapshots/solver__unsat_constrains.snap @@ -9,5 +9,4 @@ The following packages are incompatible │ └─ b 50 └─ c * cannot be installed because there are no viable options: └─ c 8 | 10 would constrain - └─ b >=0, <50 , which conflicts with any installable versions previously reported - + └─ b >=0, <50, which conflicts with any installable versions previously reported diff --git a/tests/snapshots/solver__unsat_constrains_2.snap b/tests/snapshots/solver__unsat_constrains_2.snap index 6ce7281..23227c3 100644 --- a/tests/snapshots/solver__unsat_constrains_2.snap +++ b/tests/snapshots/solver__unsat_constrains_2.snap @@ -9,9 +9,8 @@ The following packages are incompatible ├─ b 2 would require │ └─ c >=2, <3, which cannot be installed because there are no viable options: │ └─ c 2 would constrain - │ └─ a >=3, <4 , which conflicts with any installable versions previously reported + │ └─ a >=3, <4, which conflicts with any installable versions previously reported └─ b 1 would require └─ c >=1, <2, which cannot be installed because there are no viable options: └─ c 1 would constrain - └─ a >=3, <4 , which conflicts with any installable versions previously reported - + └─ a >=3, <4, which conflicts with any installable versions previously reported diff --git a/tests/solver.rs b/tests/solver.rs index 61494c7..389c1c9 100644 --- a/tests/solver.rs +++ b/tests/solver.rs @@ -21,12 +21,13 @@ use insta::assert_snapshot; use itertools::Itertools; use resolvo::{ snapshot::{DependencySnapshot, SnapshotProvider}, - utils::{Pool, Range}, + utils::Pool, Candidates, Dependencies, DependencyProvider, Interner, KnownDependencies, NameId, Problem, Requirement, SolvableId, Solver, SolverCache, StringId, UnsolvableOrCancelled, VersionSetId, VersionSetUnionId, }; use tracing_test::traced_test; +use version_ranges::Ranges; // Let's define our own packaging version system and dependency specification. // This is a very simple version system, where a package is identified by a name @@ -39,7 +40,7 @@ use tracing_test::traced_test; // means the range from 0..1 (excluding the end) // // Lets call the tuples of (Name, Version) a `Pack` and the tuples of (Name, -// Range) a `Spec` +// Ranges) a `Spec` // // We also need to create a custom provider that tells us how to sort the // candidates. This is unique to each packaging ecosystem. Let's call our @@ -111,11 +112,11 @@ impl FromStr for Pack { #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct Spec { name: String, - versions: Range, + versions: Ranges, } impl Spec { - pub fn new(name: String, versions: Range) -> Self { + pub fn new(name: String, versions: Ranges) -> Self { Self { name, versions } } @@ -138,7 +139,7 @@ impl FromStr for Spec { .expect("spec does not have a name") .to_string(); - fn version_range(s: Option<&&str>) -> Range { + fn version_range(s: Option<&&str>) -> Ranges { if let Some(s) = s { let (start, end) = s .split_once("..") @@ -149,9 +150,9 @@ impl FromStr for Spec { .transpose() .unwrap() .unwrap_or(start.offset(1)); - Range::between(start, end) + Ranges::between(start, end) } else { - Range::full() + Ranges::full() } } @@ -164,7 +165,7 @@ impl FromStr for Spec { /// This provides sorting functionality for our `BundleBox` packaging system #[derive(Default)] struct BundleBoxProvider { - pool: Pool>, + pool: Pool>, packages: IndexMap>, favored: HashMap, locked: HashMap,