Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a VersionSet trait instead of Range #108

Merged
merged 16 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion examples/branching_error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
use pubgrub::solver::{resolve, OfflineDependencyProvider};
use pubgrub::version::SemanticVersion;

type SemVS = Range<SemanticVersion>;

// https://github.com/dart-lang/pub/blob/master/doc/solver.md#branching-error-reporting
fn main() {
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
#[rustfmt::skip]
// root 1.0.0 depends on foo ^1.0.0
dependency_provider.add_dependencies(
Expand Down
27 changes: 16 additions & 11 deletions examples/caching_dependency_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ use std::error::Error;
use pubgrub::package::Package;
use pubgrub::range::Range;
use pubgrub::solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider};
use pubgrub::version::{NumberVersion, Version};
use pubgrub::version::NumberVersion;
use pubgrub::version_set::VersionSet;

type NumVS = Range<NumberVersion>;

// An example implementing caching dependency provider that will
// store queried dependencies in memory and check them before querying more from remote.
struct CachingDependencyProvider<P: Package, V: Version, DP: DependencyProvider<P, V>> {
struct CachingDependencyProvider<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> {
remote_dependencies: DP,
cached_dependencies: RefCell<OfflineDependencyProvider<P, V>>,
cached_dependencies: RefCell<OfflineDependencyProvider<P, VS>>,
}

impl<P: Package, V: Version, DP: DependencyProvider<P, V>> CachingDependencyProvider<P, V, DP> {
impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>>
CachingDependencyProvider<P, VS, DP>
{
pub fn new(remote_dependencies_provider: DP) -> Self {
CachingDependencyProvider {
remote_dependencies: remote_dependencies_provider,
Expand All @@ -24,22 +29,22 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> CachingDependencyProv
}
}

impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P, V>
for CachingDependencyProvider<P, V, DP>
impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> DependencyProvider<P, VS>
for CachingDependencyProvider<P, VS, DP>
{
fn choose_package_version<T: std::borrow::Borrow<P>, U: std::borrow::Borrow<Range<V>>>(
fn choose_package_version<T: std::borrow::Borrow<P>, U: std::borrow::Borrow<VS>>(
&self,
packages: impl Iterator<Item = (T, U)>,
) -> Result<(T, Option<V>), Box<dyn Error>> {
) -> Result<(T, Option<VS::V>), Box<dyn Error>> {
self.remote_dependencies.choose_package_version(packages)
}

// Caches dependencies if they were already queried
fn get_dependencies(
&self,
package: &P,
version: &V,
) -> Result<Dependencies<P, V>, Box<dyn Error>> {
version: &VS::V,
) -> Result<Dependencies<P, VS>, Box<dyn Error>> {
let mut cache = self.cached_dependencies.borrow_mut();
match cache.get_dependencies(package, version) {
Ok(Dependencies::Unknown) => {
Expand All @@ -65,7 +70,7 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P,

fn main() {
// Simulating remote provider locally.
let mut remote_dependencies_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
let mut remote_dependencies_provider = OfflineDependencyProvider::<&str, NumVS>::new();

// Add dependencies as needed. Here only root package is added.
remote_dependencies_provider.add_dependencies("root", 1, Vec::new());
Expand Down
4 changes: 3 additions & 1 deletion examples/doc_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use pubgrub::range::Range;
use pubgrub::solver::{resolve, OfflineDependencyProvider};
use pubgrub::version::NumberVersion;

type NumVS = Range<NumberVersion>;

// `root` depends on `menu` and `icons`
// `menu` depends on `dropdown`
// `dropdown` depends on `icons`
// `icons` has no dependency
#[rustfmt::skip]
fn main() {
let mut dependency_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
let mut dependency_provider = OfflineDependencyProvider::<&str, NumVS>::new();
dependency_provider.add_dependencies(
"root", 1, [("menu", Range::any()), ("icons", Range::any())],
);
Expand Down
4 changes: 3 additions & 1 deletion examples/doc_interface_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
use pubgrub::solver::{resolve, OfflineDependencyProvider};
use pubgrub::version::SemanticVersion;

type SemVS = Range<SemanticVersion>;

// `root` depends on `menu`, `icons 1.0.0` and `intl 5.0.0`
// `menu 1.0.0` depends on `dropdown < 2.0.0`
// `menu >= 1.1.0` depends on `dropdown >= 2.0.0`
Expand All @@ -15,7 +17,7 @@ use pubgrub::version::SemanticVersion;
// `intl` has no dependency
#[rustfmt::skip]
fn main() {
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
// Direct dependencies: menu and icons.
dependency_provider.add_dependencies("root", (1, 0, 0), [
("menu", Range::any()),
Expand Down
4 changes: 3 additions & 1 deletion examples/doc_interface_semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
use pubgrub::solver::{resolve, OfflineDependencyProvider};
use pubgrub::version::SemanticVersion;

type SemVS = Range<SemanticVersion>;

// `root` depends on `menu` and `icons 1.0.0`
// `menu 1.0.0` depends on `dropdown < 2.0.0`
// `menu >= 1.1.0` depends on `dropdown >= 2.0.0`
Expand All @@ -14,7 +16,7 @@ use pubgrub::version::SemanticVersion;
// `icons` has no dependency
#[rustfmt::skip]
fn main() {
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
// Direct dependencies: menu and icons.
dependency_provider.add_dependencies("root", (1, 0, 0), [
("menu", Range::any()),
Expand Down
4 changes: 3 additions & 1 deletion examples/linear_error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
use pubgrub::solver::{resolve, OfflineDependencyProvider};
use pubgrub::version::SemanticVersion;

type SemVS = Range<SemanticVersion>;

// https://github.com/dart-lang/pub/blob/master/doc/solver.md#linear-error-reporting
fn main() {
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
#[rustfmt::skip]
// root 1.0.0 depends on foo ^1.0.0 and baz ^1.0.0
dependency_provider.add_dependencies(
Expand Down
12 changes: 6 additions & 6 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use thiserror::Error;

use crate::package::Package;
use crate::report::DerivationTree;
use crate::version::Version;
use crate::version_set::VersionSet;

/// Errors that may occur while solving dependencies.
#[derive(Error, Debug)]
pub enum PubGrubError<P: Package, V: Version> {
pub enum PubGrubError<P: Package, VS: VersionSet> {
/// There is no solution for this set of dependencies.
#[error("No solution")]
NoSolution(DerivationTree<P, V>),
NoSolution(DerivationTree<P, VS>),

/// Error arising when the implementer of
/// [DependencyProvider](crate::solver::DependencyProvider)
Expand All @@ -24,7 +24,7 @@ pub enum PubGrubError<P: Package, V: Version> {
/// Package whose dependencies we want.
package: P,
/// Version of the package for which we want the dependencies.
version: V,
version: VS::V,
/// Error raised by the implementer of
/// [DependencyProvider](crate::solver::DependencyProvider).
source: Box<dyn std::error::Error>,
Expand All @@ -40,7 +40,7 @@ pub enum PubGrubError<P: Package, V: Version> {
/// Package whose dependencies we want.
package: P,
/// Version of the package for which we want the dependencies.
version: V,
version: VS::V,
/// The dependent package that requires us to pick from the empty set.
dependent: P,
},
Expand All @@ -55,7 +55,7 @@ pub enum PubGrubError<P: Package, V: Version> {
/// Package whose dependencies we want.
package: P,
/// Version of the package for which we want the dependencies.
version: V,
version: VS::V,
},

/// Error arising when the implementer of
Expand Down
45 changes: 22 additions & 23 deletions src/internal/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,37 @@ use crate::internal::partial_solution::{DecisionLevel, PartialSolution};
use crate::internal::small_vec::SmallVec;
use crate::package::Package;
use crate::report::DerivationTree;
use crate::solver::DependencyConstraints;
use crate::type_aliases::Map;
use crate::version::Version;
use crate::type_aliases::{DependencyConstraints, Map};
use crate::version_set::VersionSet;

/// Current state of the PubGrub algorithm.
#[derive(Clone)]
pub struct State<P: Package, V: Version> {
pub struct State<P: Package, VS: VersionSet> {
root_package: P,
root_version: V,
root_version: VS::V,

incompatibilities: Map<P, Vec<IncompId<P, V>>>,
incompatibilities: Map<P, Vec<IncompId<P, VS>>>,

/// Store the ids of incompatibilities that are already contradicted
/// and will stay that way until the next conflict and backtrack is operated.
contradicted_incompatibilities: rustc_hash::FxHashSet<IncompId<P, V>>,
contradicted_incompatibilities: rustc_hash::FxHashSet<IncompId<P, VS>>,

/// Partial solution.
/// TODO: remove pub.
pub partial_solution: PartialSolution<P, V>,
pub partial_solution: PartialSolution<P, VS>,

/// The store is the reference storage for all incompatibilities.
pub incompatibility_store: Arena<Incompatibility<P, V>>,
pub incompatibility_store: Arena<Incompatibility<P, VS>>,

/// This is a stack of work to be done in `unit_propagation`.
/// It can definitely be a local variable to that method, but
/// this way we can reuse the same allocation for better performance.
unit_propagation_buffer: SmallVec<P>,
}

impl<P: Package, V: Version> State<P, V> {
impl<P: Package, VS: VersionSet> State<P, VS> {
/// Initialization of PubGrub state.
pub fn init(root_package: P, root_version: V) -> Self {
pub fn init(root_package: P, root_version: VS::V) -> Self {
let mut incompatibility_store = Arena::new();
let not_root_id = incompatibility_store.alloc(Incompatibility::not_root(
root_package.clone(),
Expand All @@ -66,7 +65,7 @@ impl<P: Package, V: Version> State<P, V> {
}

/// Add an incompatibility to the state.
pub fn add_incompatibility(&mut self, incompat: Incompatibility<P, V>) {
pub fn add_incompatibility(&mut self, incompat: Incompatibility<P, VS>) {
let id = self.incompatibility_store.alloc(incompat);
self.merge_incompatibility(id);
}
Expand All @@ -75,9 +74,9 @@ impl<P: Package, V: Version> State<P, V> {
pub fn add_incompatibility_from_dependencies(
&mut self,
package: P,
version: V,
deps: &DependencyConstraints<P, V>,
) -> std::ops::Range<IncompId<P, V>> {
version: VS::V,
deps: &DependencyConstraints<P, VS>,
) -> std::ops::Range<IncompId<P, VS>> {
// Create incompatibilities and allocate them in the store.
let new_incompats_id_range = self
.incompatibility_store
Expand All @@ -92,13 +91,13 @@ impl<P: Package, V: Version> State<P, V> {
}

/// Check if an incompatibility is terminal.
pub fn is_terminal(&self, incompatibility: &Incompatibility<P, V>) -> bool {
pub fn is_terminal(&self, incompatibility: &Incompatibility<P, VS>) -> bool {
incompatibility.is_terminal(&self.root_package, &self.root_version)
}

/// Unit propagation is the core mechanism of the solving algorithm.
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
pub fn unit_propagation(&mut self, package: P) -> Result<(), PubGrubError<P, V>> {
pub fn unit_propagation(&mut self, package: P) -> Result<(), PubGrubError<P, VS>> {
self.unit_propagation_buffer.clear();
self.unit_propagation_buffer.push(package);
while let Some(current_package) = self.unit_propagation_buffer.pop() {
Expand Down Expand Up @@ -162,8 +161,8 @@ impl<P: Package, V: Version> State<P, V> {
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
fn conflict_resolution(
&mut self,
incompatibility: IncompId<P, V>,
) -> Result<(P, IncompId<P, V>), PubGrubError<P, V>> {
incompatibility: IncompId<P, VS>,
) -> Result<(P, IncompId<P, VS>), PubGrubError<P, VS>> {
let mut current_incompat_id = incompatibility;
let mut current_incompat_changed = false;
loop {
Expand Down Expand Up @@ -209,7 +208,7 @@ impl<P: Package, V: Version> State<P, V> {
/// Backtracking.
fn backtrack(
&mut self,
incompat: IncompId<P, V>,
incompat: IncompId<P, VS>,
incompat_changed: bool,
decision_level: DecisionLevel,
) {
Expand Down Expand Up @@ -240,7 +239,7 @@ impl<P: Package, V: Version> State<P, V> {
/// Here we do the simple stupid thing of just growing the Vec.
/// It may not be trivial since those incompatibilities
/// may already have derived others.
fn merge_incompatibility(&mut self, id: IncompId<P, V>) {
fn merge_incompatibility(&mut self, id: IncompId<P, VS>) {
for (pkg, _term) in self.incompatibility_store[id].iter() {
self.incompatibilities
.entry(pkg.clone())
Expand All @@ -251,12 +250,12 @@ impl<P: Package, V: Version> State<P, V> {

// Error reporting #########################################################

fn build_derivation_tree(&self, incompat: IncompId<P, V>) -> DerivationTree<P, V> {
fn build_derivation_tree(&self, incompat: IncompId<P, VS>) -> DerivationTree<P, VS> {
let shared_ids = self.find_shared_ids(incompat);
Incompatibility::build_derivation_tree(incompat, &shared_ids, &self.incompatibility_store)
}

fn find_shared_ids(&self, incompat: IncompId<P, V>) -> Set<IncompId<P, V>> {
fn find_shared_ids(&self, incompat: IncompId<P, VS>) -> Set<IncompId<P, VS>> {
let mut all_ids = Set::new();
let mut shared_ids = Set::new();
let mut stack = vec![incompat];
Expand Down
Loading