diff --git a/examples/caching_dependency_provider.rs b/examples/caching_dependency_provider.rs index b9ca4e3a..f4f9c70d 100644 --- a/examples/caching_dependency_provider.rs +++ b/examples/caching_dependency_provider.rs @@ -5,6 +5,7 @@ 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}; diff --git a/src/internal/partial_solution.rs b/src/internal/partial_solution.rs index 8bd9af02..c75badad 100644 --- a/src/internal/partial_solution.rs +++ b/src/internal/partial_solution.rs @@ -3,7 +3,7 @@ //! The partial solution is the current state //! of the solution being built by the algorithm. -use crate::internal::incompatibility::{Incompatibility, PackageTerm, Relation}; +use crate::internal::incompatibility::{Incompatibility, Relation}; use crate::internal::memory::Memory; use crate::package::Package; use crate::term::Term; diff --git a/src/lib.rs b/src/lib.rs index 3e7519da..85aa9331 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,11 +98,18 @@ //! } //! } //! ``` -//! 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) +//! +//! The first method [make_decision](crate::solver::DependencyProvider::make_decision) chooses a +//! package and available version compatible with the provided options. +//! A helper function [make_fewest_versions_decision_helper](crate::solver::make_fewest_versions_decision_helper) +//! 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 02274c78..a2cc010a 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -208,6 +208,10 @@ pub trait DependencyProvider { /// as they are likely to find problems faster. Also normal is to pick the most recent matching /// version. /// + /// There is a helper function [make_fewest_versions_decision_helper] that makes it easy to + /// implement this method if you can for any package produce a iterator of the + /// available versions in preference order. + /// /// Note: the type `T` ensures that this returns an item from the `packages` argument. fn make_decision, U: Borrow>>( &self, @@ -232,6 +236,40 @@ pub trait DependencyProvider { } } +/// This is a helper function to make it easy to implement [DependencyProvider::make_decision]. +/// It takes a function `list_available_versions` that takes a package and returns a iterator +/// of the available versions in preference order. +/// The helper finds the package from the `packages` argument that that has 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 make_fewest_versions_decision_helper( + list_available_versions: F, + packages: impl Iterator, +) -> (T, Option) +where + T: Borrow

, + U: Borrow>, + I: Iterator, + F: Fn(&P) -> I, +{ + let mut package: Option<(T, _)> = None; + let mut min_key = usize::MAX; + for (p, term) in packages { + let key = list_available_versions(p.borrow()) + .filter(|v| term.borrow().contains(v.borrow())) + .count(); + + if key < min_key { + min_key = key; + package = Some((p, term)); + } + } + let (package, term) = package.expect("potential_packages gave us an empty iterator"); + let ver = + list_available_versions(package.borrow()).find(|v| term.borrow().contains(v.borrow())); + (package, ver) +} + /// A basic implementation of [DependencyProvider]. #[derive(Debug, Clone, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -301,33 +339,17 @@ impl DependencyProvider for OfflineDependencyProvi &self, packages: impl Iterator, ) -> Result<(T, Option), Box> { - let mut package: Option<(T, _)> = None; - let mut min_key = usize::MAX; - for (p, term) in packages { - let key = self - .dependencies - .get(p.borrow()) - .iter() - .flat_map(|k| k.keys()) - .filter(|&v| term.borrow().contains(v)) - .count(); - - if key < min_key { - min_key = key; - package = Some((p, term)); - } - } - let (package, term) = package.expect("potential_packages gave us an empty iterator"); - let ver = self - .dependencies - .get(package.borrow()) - .iter() - .flat_map(|k| k.keys()) - .filter(|&v| term.borrow().contains(v)) - .rev() - .next() - .cloned(); - Ok((package, ver)) + Ok(make_fewest_versions_decision_helper( + |p| { + self.dependencies + .get(p) + .into_iter() + .flat_map(|k| k.keys()) + .rev() + .cloned() + }, + packages, + )) } fn get_dependencies( diff --git a/tests/proptest.rs b/tests/proptest.rs index 6e2ac210..a4b6774c 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -2,18 +2,21 @@ use std::{collections::BTreeSet as Set, error::Error}; -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::solver::{ + make_fewest_versions_decision_helper, resolve, Dependencies, DependencyProvider, + OfflineDependencyProvider, +}; use pubgrub::{ error::PubGrubError, package::Package, report::DefaultStringReporter, report::Reporter, version::NumberVersion, version::Version, }; +use proptest::collection::{btree_map, vec}; +use proptest::prelude::*; +use proptest::sample::Index; +use proptest::string::string_regex; + use crate::sat_dependency_provider::SatResolve; mod sat_dependency_provider; @@ -28,32 +31,10 @@ impl DependencyProvider for OldestVersionsDependen &self, packages: impl Iterator, ) -> Result<(T, Option), Box> { - let mut package: Option<(T, _)> = None; - let mut min_key = usize::MAX; - for (p, term) in packages { - let key = self - .0 - .versions(p.borrow()) - .into_iter() - .flatten() - .filter(|&v| term.borrow().contains(v)) - .count(); - - if key < min_key { - min_key = key; - package = Some((p, term)); - } - } - let (package, term) = package.expect("potential_packages gave us an empty iterator"); - let ver = self - .0 - .versions(package.borrow()) - .into_iter() - .flatten() - .filter(|&v| term.borrow().contains(v)) - .next() - .cloned(); - Ok((package, ver)) + Ok(make_fewest_versions_decision_helper( + |p| self.0.versions(p).into_iter().flatten().cloned(), + packages, + )) } fn get_dependencies(&self, p: &P, v: &V) -> Result, Box> {