From 6b6bbd646960e950b374b4a091ecbfa3b8d3295d Mon Sep 17 00:00:00 2001 From: Eh2406 Date: Sun, 15 Nov 2020 21:12:54 -0500 Subject: [PATCH] feat!: let DependencyProvider prioritize dependencies --- benches/large_case.rs | 5 +- examples/caching_dependency_provider.rs | 18 +-- src/error.rs | 20 +-- src/internal/memory.rs | 7 +- src/internal/partial_solution.rs | 67 +++------- src/lib.rs | 25 ++-- src/solver.rs | 170 +++++++++++++++++------- src/term.rs | 9 ++ tests/proptest.rs | 66 ++++----- tests/sat_dependency_provider.rs | 7 +- 10 files changed, 227 insertions(+), 167 deletions(-) diff --git a/benches/large_case.rs b/benches/large_case.rs index 64135546..ad9f486d 100644 --- a/benches/large_case.rs +++ b/benches/large_case.rs @@ -4,7 +4,7 @@ use std::time::Duration; extern crate criterion; use self::criterion::*; -use pubgrub::solver::{resolve, DependencyProvider, OfflineDependencyProvider}; +use pubgrub::solver::{resolve, OfflineDependencyProvider}; use pubgrub::version::NumberVersion; fn bench_nested(c: &mut Criterion) { @@ -20,10 +20,9 @@ fn bench_nested(c: &mut Criterion) { let s = std::fs::read_to_string(&case).unwrap(); let dependency_provider: OfflineDependencyProvider = ron::de::from_str(&s).unwrap(); - let all_versions = dependency_provider.list_available_versions(&0).unwrap(); b.iter(|| { - for &n in &all_versions { + for &n in dependency_provider.versions(&0).unwrap() { let _ = resolve(&dependency_provider, 0, n); } }); diff --git a/examples/caching_dependency_provider.rs b/examples/caching_dependency_provider.rs index aac5b7e1..bac730ea 100644 --- a/examples/caching_dependency_provider.rs +++ b/examples/caching_dependency_provider.rs @@ -2,22 +2,20 @@ use std::cell::RefCell; use std::error::Error; -use std::hash::Hash; use pubgrub::package::Package; +use pubgrub::range::Range; use pubgrub::solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider}; use pubgrub::version::{NumberVersion, Version}; // An example implementing caching dependency provider that will // store queried dependencies in memory and check them before querying more from remote. -struct CachingDependencyProvider> { +struct CachingDependencyProvider> { remote_dependencies: DP, cached_dependencies: RefCell>, } -impl> - CachingDependencyProvider -{ +impl> CachingDependencyProvider { pub fn new(remote_dependencies_provider: DP) -> Self { CachingDependencyProvider { remote_dependencies: remote_dependencies_provider, @@ -26,12 +24,14 @@ impl> } } -impl> DependencyProvider +impl> DependencyProvider for CachingDependencyProvider { - // Lists only from remote for simplicity - fn list_available_versions(&self, package: &P) -> Result, Box> { - self.remote_dependencies.list_available_versions(package) + fn choose_package_version, U: std::borrow::Borrow>>( + &self, + packages: impl Iterator, + ) -> Result<(T, Option), Box> { + self.remote_dependencies.choose_package_version(packages) } // Caches dependencies if they were already queried diff --git a/src/error.rs b/src/error.rs index 8b565484..e9f5a74b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,19 +15,6 @@ pub enum PubGrubError { #[error("No solution")] NoSolution(DerivationTree), - /// Error arising when the implementer of - /// [DependencyProvider](crate::solver::DependencyProvider) - /// returned an error in the method - /// [list_available_versions](crate::solver::DependencyProvider::list_available_versions). - #[error("Retrieving available versions of package {package} failed")] - ErrorRetrievingVersions { - /// Package for which we want the list of versions. - package: P, - /// Error raised by the implementer of - /// [DependencyProvider](crate::solver::DependencyProvider). - source: Box, - }, - /// Error arising when the implementer of /// [DependencyProvider](crate::solver::DependencyProvider) /// returned an error in the method @@ -71,6 +58,13 @@ pub enum PubGrubError { version: V, }, + /// Error arising when the implementer of + /// [DependencyProvider](crate::solver::DependencyProvider) + /// returned an error in the method + /// [choose_package_version](crate::solver::DependencyProvider::choose_package_version). + #[error("Decision making failed")] + ErrorChoosingPackageVersion(Box), + /// Error arising when the implementer of [DependencyProvider](crate::solver::DependencyProvider) /// returned an error in the method [should_cancel](crate::solver::DependencyProvider::should_cancel). #[error("We should cancel")] diff --git a/src/internal/memory.rs b/src/internal/memory.rs index f403de42..090e51e9 100644 --- a/src/internal/memory.rs +++ b/src/internal/memory.rs @@ -5,6 +5,7 @@ use crate::internal::assignment::Assignment::{self, Decision, Derivation}; use crate::package::Package; +use crate::range::Range; use crate::term::Term; use crate::type_aliases::{Map, SelectedDependencies}; use crate::version::Version; @@ -102,7 +103,7 @@ impl Memory { /// selected version (no "decision") /// and if it contains at least one positive derivation term /// in the partial solution. - pub fn potential_packages(&mut self) -> impl Iterator)> { + pub fn potential_packages(&mut self) -> impl Iterator)> { self.assignments .iter_mut() .filter_map(|(p, pa)| pa.potential_package_filter(p)) @@ -160,7 +161,7 @@ impl PackageAssignments { fn potential_package_filter<'a, P: Package>( &'a mut self, package: &'a P, - ) -> Option<(&'a P, &'a Term)> { + ) -> Option<(&'a P, &'a Range)> { match self { PackageAssignments::Decision(_) => None, PackageAssignments::Derivations { @@ -169,7 +170,7 @@ impl PackageAssignments { } => { if intersected.is_positive() || not_intersected_yet.iter().any(|t| t.is_positive()) { - Some((package, self.assignment_intersection())) + Some((package, self.assignment_intersection().unwrap_positive())) } else { None } diff --git a/src/internal/partial_solution.rs b/src/internal/partial_solution.rs index 5bd9fd47..5ede989f 100644 --- a/src/internal/partial_solution.rs +++ b/src/internal/partial_solution.rs @@ -3,20 +3,14 @@ //! The partial solution is the current state //! of the solution being built by the algorithm. -use crate::internal::incompatibility::PackageTerm; +use crate::internal::assignment::Assignment::{self, Decision, Derivation}; +use crate::internal::incompatibility::{Incompatibility, Relation}; use crate::internal::memory::Memory; use crate::package::Package; +use crate::range::Range; use crate::term::Term; use crate::type_aliases::{Map, SelectedDependencies}; use crate::version::Version; -use crate::{ - error::PubGrubError, - internal::incompatibility::{Incompatibility, Relation}, -}; -use crate::{ - internal::assignment::Assignment::{self, Decision, Derivation}, - solver::DependencyProvider, -}; #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct DecisionLevel(u32); @@ -99,6 +93,11 @@ impl PartialSolution { self.memory.extract_solution() } + /// Compute, cache and retrieve the intersection of all terms for this package. + pub fn term_intersection_for_package(&mut self, package: &P) -> Option<&Term> { + self.memory.term_intersection_for_package(package) + } + /// Backtrack the partial solution to a given decision level. pub fn backtrack(&mut self, decision_level: DecisionLevel) { // TODO: improve with dichotomic search. @@ -121,49 +120,15 @@ impl PartialSolution { partial_solution } - /// Heuristic to pick the next package to add to the partial solution. - /// This should be a package with a positive derivation but no decision yet. - /// If multiple choices are possible, use a heuristic. - /// - /// Current heuristic employed by this and Pub's implementations is to choose - /// the package with the fewest versions matching the outstanding constraint. - /// This tends to find conflicts earlier if any exist, - /// since these packages will run out of versions to try more quickly. - pub fn pick_package( - &mut self, - dependency_provider: &impl DependencyProvider, - ) -> Result>, PubGrubError> { - let mut out: Option> = None; - let mut min_key = usize::MAX; - for (p, term) in self.memory.potential_packages() { - let key = dependency_provider - .list_available_versions(p) - .map_err(|err| PubGrubError::ErrorRetrievingVersions { - package: p.clone(), - source: err, - })? - .iter() - .filter(|&v| term.contains(v)) - .count(); - if key < min_key { - min_key = key; - out = Some((p.clone(), term.clone())); - } + /// Extract potential packages for the next iteration of unit propagation. + /// Return `None` if there is no suitable package anymore, which stops the algorithm. + pub fn potential_packages(&mut self) -> Option)>> { + let mut iter = self.memory.potential_packages().peekable(); + if iter.peek().is_some() { + Some(iter) + } else { + None } - Ok(out) - } - - /// Pub chooses the latest matching version of the package - /// that match the outstanding constraint. - /// - /// Here we just pick the first one that satisfies the terms. - /// It is the responsibility of the provider of `available_versions` - /// to list them with preferred versions first. - pub fn pick_version(available_versions: &[V], partial_solution_term: &Term) -> Option { - available_versions - .iter() - .find(|v| partial_solution_term.contains(v)) - .cloned() } /// We can add the version to the partial solution as a decision diff --git a/src/lib.rs b/src/lib.rs index b7cb706e..759328b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,15 +77,15 @@ //! ``` //! # use pubgrub::solver::{DependencyProvider, Dependencies}; //! # use pubgrub::version::SemanticVersion; +//! # use pubgrub::range::Range; +//! # use pubgrub::type_aliases::Map; //! # use std::error::Error; +//! # use std::borrow::Borrow; //! # //! # struct MyDependencyProvider; //! # //! impl DependencyProvider for MyDependencyProvider { -//! fn list_available_versions( -//! &self, -//! package: &String -//! ) -> Result, Box> { +//! fn choose_package_version, U: Borrow>>(&self,packages: impl Iterator) -> Result<(T, Option), Box> { //! unimplemented!() //! } //! @@ -98,11 +98,20 @@ //! } //! } //! ``` +//! //! The first method -//! [list_available_versions](crate::solver::DependencyProvider::list_available_versions) -//! should return all available versions of a package. -//! The second method -//! [get_dependencies](crate::solver::DependencyProvider::get_dependencies) +//! [choose_package_version](crate::solver::DependencyProvider::choose_package_version) +//! chooses a package and available version compatible with the provided options. +//! A helper function +//! [choose_package_with_fewest_versions](crate::solver::choose_package_with_fewest_versions) +//! is provided for convenience +//! in cases when lists of available versions for packages are easily obtained. +//! The strategy of that helper function consists in choosing the package +//! with the fewest number of compatible versions to speed up resolution. +//! But in general you are free to employ whatever strategy suits you best +//! to pick a package and a version. +//! +//! The second method [get_dependencies](crate::solver::DependencyProvider::get_dependencies) //! aims at retrieving the dependencies of a given package at a given version. //! Returns [None] if dependencies are unknown. //! diff --git a/src/solver.rs b/src/solver.rs index eb9a4561..a99f0f47 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -65,14 +65,13 @@ //! to satisfy the dependencies of that package and version pair. //! If there is no solution, the reason will be provided as clear as possible. -use std::collections::BTreeSet as Set; +use std::borrow::Borrow; +use std::collections::{BTreeMap, BTreeSet as Set}; use std::error::Error; -use std::hash::Hash; use crate::error::PubGrubError; use crate::internal::core::State; use crate::internal::incompatibility::Incompatibility; -use crate::internal::partial_solution::PartialSolution; use crate::package::Package; use crate::range::Range; use crate::type_aliases::{Map, SelectedDependencies}; @@ -95,43 +94,56 @@ pub fn resolve( state.unit_propagation(next)?; - // Pick the next package. - let (p, term) = match state.partial_solution.pick_package(dependency_provider)? { - None => { - return state.partial_solution.extract_solution().ok_or_else(|| { - PubGrubError::Failure( - "How did we end up with no package to choose but no solution?".into(), - ) - }) - } - Some(x) => x, - }; - next = p.clone(); - let available_versions = - dependency_provider - .list_available_versions(&p) - .map_err(|err| PubGrubError::ErrorRetrievingVersions { - package: p.clone(), - source: err, - })?; + let potential_packages = state.partial_solution.potential_packages(); + if potential_packages.is_none() { + drop(potential_packages); + // The borrow checker did not like using a match on potential_packages. + // This `if ... is_none ... drop` is a workaround. + // I believe this is a case where Polonius could help, when and if it lands in rustc. + return state.partial_solution.extract_solution().ok_or_else(|| { + PubGrubError::Failure( + "How did we end up with no package to choose but no solution?".into(), + ) + }); + } + let decision = dependency_provider + .choose_package_version(potential_packages.unwrap()) + .map_err(PubGrubError::ErrorChoosingPackageVersion)?; + next = decision.0.clone(); // Pick the next compatible version. - let v = match PartialSolution::::pick_version(&available_versions[..], &term) { + let v = match decision.1 { None => { + let term = state + .partial_solution + .term_intersection_for_package(&next) + .expect("a package was chosen but we don't have a term.") + .clone(); state.add_incompatibility(|id| { - Incompatibility::no_versions(id, p.clone(), term.clone()) + Incompatibility::no_versions(id, next.clone(), term.clone()) }); continue; } Some(x) => x, }; + if !state + .partial_solution + .term_intersection_for_package(&next) + .expect("a package was chosen but we don't have a term.") + .contains(&v) + { + return Err(PubGrubError::ErrorChoosingPackageVersion( + "choose_package_version picked an incompatible version".into(), + )); + } if added_dependencies - .entry(p.clone()) + .entry(next.clone()) .or_default() .insert(v.clone()) { // Retrieve that package dependencies. + let p = &next; let dependencies = match dependency_provider .get_dependencies(&p, &v) @@ -182,11 +194,13 @@ pub fn resolve( "Root package depends on itself at a different version?".into(), )); } - state.partial_solution.add_version(p, v, &dep_incompats); + state + .partial_solution + .add_version(p.clone(), v, &dep_incompats); } else { // `dep_incompats` are already in `incompatibilities` so we know there are not satisfied // terms and can add the decision directly. - state.partial_solution.add_decision(p, v); + state.partial_solution.add_decision(next.clone(), v); } } } @@ -212,10 +226,35 @@ pub type DependencyConstraints = Map>; /// Trait that allows the algorithm to retrieve available packages and their dependencies. /// An implementor needs to be supplied to the [resolve] function. pub trait DependencyProvider { - /// Lists available versions for a given package. - /// The strategy of which version should be preferably picked in the list of available versions - /// is implied by the order of the list: first version in the list will be tried first. - fn list_available_versions(&self, package: &P) -> Result, Box>; + /// [Decision making](https://github.com/dart-lang/pub/blob/master/doc/solver.md#decision-making) + /// is the process of choosing the next package + /// and version that will be appended to the partial solution. + /// Every time such a decision must be made, + /// potential valid packages and version ranges are preselected by the resolver, + /// and the dependency provider must choose. + /// + /// The strategy employed to choose such package and version + /// cannot change the existence of a solution or not, + /// but can drastically change the performances of the solver, + /// or the properties of the solution. + /// The documentation of Pub (PubGrub implementation for the dart programming language) + /// states the following: + /// + /// > Pub chooses the latest matching version of the package + /// > with the fewest versions that match the outstanding constraint. + /// > This tends to find conflicts earlier if any exist, + /// > since these packages will run out of versions to try more quickly. + /// > But there's likely room for improvement in these heuristics. + /// + /// A helper function [choose_package_with_fewest_versions] is provided to ease + /// implementations of this method if you can produce an iterator + /// of the available versions in preference order for any package. + /// + /// Note: the type `T` ensures that this returns an item from the `packages` argument. + fn choose_package_version, U: Borrow>>( + &self, + potential_packages: impl Iterator, + ) -> Result<(T, Option), Box>; /// Retrieves the package dependencies. /// Return [Dependencies::Unknown] if its dependencies are unknown. @@ -235,15 +274,45 @@ pub trait DependencyProvider { } } +/// This is a helper function to make it easy to implement +/// [DependencyProvider::choose_package_version]. +/// It takes a function `list_available_versions` that takes a package and returns an iterator +/// of the available versions in preference order. +/// The helper finds the package from the `packages` argument with the fewest versions from +/// `list_available_versions` contained in the constraints. Then takes that package and finds the +/// first version contained in the constraints. +pub fn choose_package_with_fewest_versions( + list_available_versions: F, + potential_packages: impl Iterator, +) -> (T, Option) +where + T: Borrow

, + U: Borrow>, + I: Iterator, + F: Fn(&P) -> I, +{ + let count_valid = |(p, range): &(T, U)| { + list_available_versions(p.borrow()) + .filter(|v| range.borrow().contains(v.borrow())) + .count() + }; + let (pkg, range) = potential_packages + .min_by_key(count_valid) + .expect("potential_packages gave us an empty iterator"); + let version = + list_available_versions(pkg.borrow()).find(|v| range.borrow().contains(v.borrow())); + (pkg, version) +} + /// A basic implementation of [DependencyProvider]. #[derive(Debug, Clone, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(transparent))] -pub struct OfflineDependencyProvider { - dependencies: Map>>, +pub struct OfflineDependencyProvider { + dependencies: Map>>, } -impl OfflineDependencyProvider { +impl OfflineDependencyProvider { /// Creates an empty OfflineDependencyProvider with no dependencies. pub fn new() -> Self { Self { @@ -282,12 +351,10 @@ impl OfflineDependencyProvider { self.dependencies.keys() } - /// Lists versions of saved packages. + /// Lists versions of saved packages in sorted order. /// Returns [None] if no information is available regarding that package. - fn versions(&self, package: &P) -> Option> { - self.dependencies - .get(package) - .map(|k| k.keys().cloned().collect()) + pub fn versions(&self, package: &P) -> Option> { + self.dependencies.get(package).map(|k| k.keys()) } /// Lists dependencies of a given package and version. @@ -299,13 +366,24 @@ impl OfflineDependencyProvider { /// An implementation of [DependencyProvider] that /// contains all dependency information available in memory. -/// Versions are listed with the newest versions first. -impl DependencyProvider for OfflineDependencyProvider { - fn list_available_versions(&self, package: &P) -> Result, Box> { - Ok(self - .versions(package) - .map(|v| v.into_iter().rev().collect()) - .unwrap_or_default()) +/// Packages are picked with the fewest versions contained in the constraints first. +/// Versions are picked with the newest versions first. +impl DependencyProvider for OfflineDependencyProvider { + fn choose_package_version, U: Borrow>>( + &self, + potential_packages: impl Iterator, + ) -> Result<(T, Option), Box> { + Ok(choose_package_with_fewest_versions( + |p| { + self.dependencies + .get(p) + .into_iter() + .flat_map(|k| k.keys()) + .rev() + .cloned() + }, + potential_packages, + )) } fn get_dependencies( diff --git a/src/term.rs b/src/term.rs index 789222f1..63c4da7d 100644 --- a/src/term.rs +++ b/src/term.rs @@ -62,6 +62,15 @@ impl Term { Self::Negative(range) => !(range.contains(v)), } } + + /// Unwrap the range contains in a positive term. + /// Will panic if used on a negative range. + pub(crate) fn unwrap_positive(&self) -> &Range { + match self { + Self::Positive(range) => range, + _ => panic!("Negative term cannot unwrap positive range"), + } + } } /// Set operations with terms. diff --git a/tests/proptest.rs b/tests/proptest.rs index 26636fc4..7aa94a29 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -2,35 +2,39 @@ use std::{collections::BTreeSet as Set, error::Error}; +use pubgrub::error::PubGrubError; +use pubgrub::package::Package; +use pubgrub::range::Range; +use pubgrub::report::{DefaultStringReporter, Reporter}; +use pubgrub::solver::{ + choose_package_with_fewest_versions, resolve, Dependencies, DependencyProvider, + OfflineDependencyProvider, +}; +use pubgrub::version::{NumberVersion, Version}; + use proptest::collection::{btree_map, vec}; use proptest::prelude::*; use proptest::sample::Index; use proptest::string::string_regex; -use pubgrub::range::Range; -use pubgrub::solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider}; -use pubgrub::{ - error::PubGrubError, package::Package, report::DefaultStringReporter, report::Reporter, - version::NumberVersion, version::Version, -}; - use crate::sat_dependency_provider::SatResolve; mod sat_dependency_provider; -/// The same as DP but takes versions from the opposite end: -/// if DP returns versions from newest to oldest, this returns them from oldest to newest. +/// The same as [OfflineDependencyProvider] but takes versions from the opposite end: +/// if [OfflineDependencyProvider] returns versions from newest to oldest, this returns them from oldest to newest. #[derive(Clone)] -struct ReverseDependencyProvider(DP); - -impl> DependencyProvider - for ReverseDependencyProvider -{ - fn list_available_versions(&self, p: &P) -> Result, Box> { - self.0.list_available_versions(p).map(|mut v| { - v.reverse(); - v - }) +struct OldestVersionsDependencyProvider(OfflineDependencyProvider); + +impl DependencyProvider for OldestVersionsDependencyProvider { + fn choose_package_version, U: std::borrow::Borrow>>( + &self, + potential_packages: impl Iterator, + ) -> Result<(T, Option), Box> { + Ok(choose_package_with_fewest_versions( + |p| self.0.versions(p).into_iter().flatten().cloned(), + potential_packages, + )) } fn get_dependencies(&self, p: &P, v: &V) -> Result, Box> { @@ -61,8 +65,11 @@ impl TimeoutDependencyProvider { impl> DependencyProvider for TimeoutDependencyProvider { - fn list_available_versions(&self, p: &P) -> Result, Box> { - self.dp.list_available_versions(p) + fn choose_package_version, U: std::borrow::Borrow>>( + &self, + potential_packages: impl Iterator, + ) -> Result<(T, Option), Box> { + self.dp.choose_package_version(potential_packages) } fn get_dependencies(&self, p: &P, v: &V) -> Result, Box> { @@ -178,7 +185,7 @@ pub fn registry_strategy( let (a, b) = order_index(a, b, len_all_pkgid); let (a, b) = if reverse_alphabetical { (b, a) } else { (a, b) }; let ((dep_name, _), _) = list_of_pkgid[a].to_owned(); - if &(list_of_pkgid[b].0).0 == &dep_name { + if (list_of_pkgid[b].0).0 == dep_name { continue; } let s = &crate_vers_by_name[&dep_name]; @@ -341,7 +348,7 @@ proptest! { fn prop_reversed_version_errors_the_same( (dependency_provider, cases) in registry_strategy(0u16..665, 666) ) { - let reverse_provider = ReverseDependencyProvider(dependency_provider.clone()); + let reverse_provider = OldestVersionsDependencyProvider(dependency_provider.clone()); for (name, ver) in cases { let l = resolve(&TimeoutDependencyProvider::new(dependency_provider.clone(), 50_000), name, ver); let r = resolve(&TimeoutDependencyProvider::new(reverse_provider.clone(), 50_000), name, ver); @@ -362,9 +369,9 @@ proptest! { let mut removed_provider = dependency_provider.clone(); for (package_idx, version_idx, dep_idx) in indexes_to_remove { let package = package_idx.get(&packages); - let versions = dependency_provider - .list_available_versions(package) - .unwrap(); + let versions: Vec<_> = dependency_provider + .versions(package) + .unwrap().collect(); let version = version_idx.get(&versions); let dependencies: Vec<(u16, Range)> = match dependency_provider .get_dependencies(package, version) @@ -377,7 +384,7 @@ proptest! { let dependency = dep_idx.get(&dependencies).0; removed_provider.add_dependencies( **package, - *version, + **version, dependencies.into_iter().filter(|x| x.0 != dependency), ) } @@ -414,10 +421,9 @@ proptest! { .packages() .flat_map(|&p| { dependency_provider - .list_available_versions(&p) + .versions(&p) .unwrap() - .into_iter() - .map(move |v| (p, v)) + .map(move |v| (p, v.clone())) }) .collect(); let to_remove: Set<(_, _)> = indexes_to_remove.iter().map(|x| x.get(&all_versions)).cloned().collect(); diff --git a/tests/sat_dependency_provider.rs b/tests/sat_dependency_provider.rs index 62c306b4..425759a2 100644 --- a/tests/sat_dependency_provider.rs +++ b/tests/sat_dependency_provider.rs @@ -4,7 +4,6 @@ use pubgrub::package::Package; use pubgrub::solver::{Dependencies, DependencyProvider, OfflineDependencyProvider}; use pubgrub::type_aliases::{Map, SelectedDependencies}; use pubgrub::version::Version; -use std::hash::Hash; use varisat::ExtendFormula; const fn num_bits() -> usize { @@ -52,7 +51,7 @@ pub struct SatResolve { all_versions_by_p: Map>, } -impl SatResolve { +impl SatResolve { pub fn new(dp: &OfflineDependencyProvider) -> Self { let mut cnf = varisat::CnfFormula::new(); @@ -61,14 +60,14 @@ impl SatResolve { for p in dp.packages() { let mut versions_for_p = vec![]; - for v in dp.list_available_versions(p).unwrap() { + for v in dp.versions(p).unwrap() { let new_var = cnf.new_var(); all_versions.push((p.clone(), v.clone(), new_var)); versions_for_p.push(new_var); all_versions_by_p .entry(p.clone()) .or_default() - .push((v, new_var)); + .push((v.clone(), new_var)); } // no two versions of the same package sat_at_most_one(&mut cnf, &versions_for_p);