Skip to content

Commit

Permalink
feat: add a make_fewest_versions_decision_helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Eh2406 committed Nov 12, 2020
1 parent 9eccf8e commit eb3d094
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 65 deletions.
1 change: 1 addition & 0 deletions examples/caching_dependency_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
2 changes: 1 addition & 1 deletion src/internal/partial_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
17 changes: 12 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//!
Expand Down
76 changes: 49 additions & 27 deletions src/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ pub trait DependencyProvider<P: Package, V: Version> {
/// 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<T: Borrow<P>, U: Borrow<Range<V>>>(
&self,
Expand All @@ -232,6 +236,40 @@ pub trait DependencyProvider<P: Package, V: Version> {
}
}

/// 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<P: Package, V: Version, T, U, I, F>(
list_available_versions: F,
packages: impl Iterator<Item = (T, U)>,
) -> (T, Option<V>)
where
T: Borrow<P>,
U: Borrow<Range<V>>,
I: Iterator<Item = V>,
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))]
Expand Down Expand Up @@ -301,33 +339,17 @@ impl<P: Package, V: Version> DependencyProvider<P, V> for OfflineDependencyProvi
&self,
packages: impl Iterator<Item = (T, U)>,
) -> Result<(T, Option<V>), Box<dyn Error>> {
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(
Expand Down
45 changes: 13 additions & 32 deletions tests/proptest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,32 +31,10 @@ impl<P: Package, V: Version> DependencyProvider<P, V> for OldestVersionsDependen
&self,
packages: impl Iterator<Item = (T, U)>,
) -> Result<(T, Option<V>), Box<dyn Error>> {
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<Dependencies<P, V>, Box<dyn Error>> {
Expand Down

0 comments on commit eb3d094

Please sign in to comment.