Skip to content

Commit 71385b7

Browse files
authored
feat!: associated types instead of generics for P and VS (pubgrub-rs#190)
* feat!: associated types instead of generics for P and VS * mabe V as well? * more source * more refs to Version trait
1 parent c4d209a commit 71385b7

File tree

13 files changed

+218
-153
lines changed

13 files changed

+218
-153
lines changed

benches/large_case.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ fn bench<'a, P: Package + Deserialize<'a>, VS: VersionSet + Deserialize<'a>>(
1717
) where
1818
<VS as VersionSet>::V: Deserialize<'a>,
1919
{
20-
let dependency_provider: OfflineDependencyProvider<P, VS> = ron::de::from_str(&case).unwrap();
20+
let dependency_provider: OfflineDependencyProvider<P, VS> = ron::de::from_str(case).unwrap();
2121

2222
b.iter(|| {
2323
for p in dependency_provider.packages() {

examples/caching_dependency_provider.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,19 @@
22

33
use std::cell::RefCell;
44

5-
use pubgrub::package::Package;
65
use pubgrub::range::Range;
76
use pubgrub::solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider};
8-
use pubgrub::version_set::VersionSet;
97

108
type NumVS = Range<u32>;
119

1210
// An example implementing caching dependency provider that will
1311
// store queried dependencies in memory and check them before querying more from remote.
14-
struct CachingDependencyProvider<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> {
12+
struct CachingDependencyProvider<DP: DependencyProvider> {
1513
remote_dependencies: DP,
16-
cached_dependencies: RefCell<OfflineDependencyProvider<P, VS>>,
14+
cached_dependencies: RefCell<OfflineDependencyProvider<DP::P, DP::VS>>,
1715
}
1816

19-
impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>>
20-
CachingDependencyProvider<P, VS, DP>
21-
{
17+
impl<DP: DependencyProvider> CachingDependencyProvider<DP> {
2218
pub fn new(remote_dependencies_provider: DP) -> Self {
2319
CachingDependencyProvider {
2420
remote_dependencies: remote_dependencies_provider,
@@ -27,15 +23,13 @@ impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>>
2723
}
2824
}
2925

30-
impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> DependencyProvider<P, VS>
31-
for CachingDependencyProvider<P, VS, DP>
32-
{
26+
impl<DP: DependencyProvider> DependencyProvider for CachingDependencyProvider<DP> {
3327
// Caches dependencies if they were already queried
3428
fn get_dependencies(
3529
&self,
36-
package: &P,
37-
version: &VS::V,
38-
) -> Result<Dependencies<P, VS>, DP::Err> {
30+
package: &DP::P,
31+
version: &DP::V,
32+
) -> Result<Dependencies<DP::P, DP::VS>, DP::Err> {
3933
let mut cache = self.cached_dependencies.borrow_mut();
4034
match cache.get_dependencies(package, version) {
4135
Ok(Dependencies::Unknown) => {
@@ -58,17 +52,21 @@ impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> DependencyProvid
5852
}
5953
}
6054

61-
fn choose_version(&self, package: &P, range: &VS) -> Result<Option<VS::V>, DP::Err> {
55+
fn choose_version(&self, package: &DP::P, range: &DP::VS) -> Result<Option<DP::V>, DP::Err> {
6256
self.remote_dependencies.choose_version(package, range)
6357
}
6458

6559
type Priority = DP::Priority;
6660

67-
fn prioritize(&self, package: &P, range: &VS) -> Self::Priority {
61+
fn prioritize(&self, package: &DP::P, range: &DP::VS) -> Self::Priority {
6862
self.remote_dependencies.prioritize(package, range)
6963
}
7064

7165
type Err = DP::Err;
66+
67+
type P = DP::P;
68+
type V = DP::V;
69+
type VS = DP::VS;
7270
}
7371

7472
fn main() {

src/error.rs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,94 @@
44
55
use thiserror::Error;
66

7-
use crate::package::Package;
87
use crate::report::DerivationTree;
9-
use crate::version_set::VersionSet;
8+
use crate::solver::DependencyProvider;
109

1110
/// Errors that may occur while solving dependencies.
12-
#[derive(Error, Debug)]
13-
pub enum PubGrubError<P: Package, VS: VersionSet, E: std::error::Error> {
11+
#[derive(Error)]
12+
pub enum PubGrubError<DP>
13+
where
14+
DP: DependencyProvider,
15+
{
1416
/// There is no solution for this set of dependencies.
1517
#[error("No solution")]
16-
NoSolution(DerivationTree<P, VS>),
18+
NoSolution(DerivationTree<DP::P, DP::VS>),
1719

1820
/// Error arising when the implementer of
19-
/// [DependencyProvider](crate::solver::DependencyProvider)
21+
/// [DependencyProvider]
2022
/// returned an error in the method
2123
/// [get_dependencies](crate::solver::DependencyProvider::get_dependencies).
2224
#[error("Retrieving dependencies of {package} {version} failed")]
2325
ErrorRetrievingDependencies {
2426
/// Package whose dependencies we want.
25-
package: P,
27+
package: DP::P,
2628
/// Version of the package for which we want the dependencies.
27-
version: VS::V,
29+
version: DP::V,
2830
/// Error raised by the implementer of
29-
/// [DependencyProvider](crate::solver::DependencyProvider).
30-
source: E,
31+
/// [DependencyProvider].
32+
source: DP::Err,
3133
},
3234

3335
/// Error arising when the implementer of
34-
/// [DependencyProvider](crate::solver::DependencyProvider)
36+
/// [DependencyProvider]
3537
/// returned a dependency on the requested package.
3638
/// This technically means that the package directly depends on itself,
3739
/// and is clearly some kind of mistake.
3840
#[error("{package} {version} depends on itself")]
3941
SelfDependency {
4042
/// Package whose dependencies we want.
41-
package: P,
43+
package: DP::P,
4244
/// Version of the package for which we want the dependencies.
43-
version: VS::V,
45+
version: DP::V,
4446
},
4547

4648
/// Error arising when the implementer of
47-
/// [DependencyProvider](crate::solver::DependencyProvider)
49+
/// [DependencyProvider]
4850
/// returned an error in the method
4951
/// [choose_version](crate::solver::DependencyProvider::choose_version).
5052
#[error("Decision making failed")]
51-
ErrorChoosingPackageVersion(E),
53+
ErrorChoosingPackageVersion(#[source] DP::Err),
5254

53-
/// Error arising when the implementer of [DependencyProvider](crate::solver::DependencyProvider)
55+
/// Error arising when the implementer of [DependencyProvider]
5456
/// returned an error in the method [should_cancel](crate::solver::DependencyProvider::should_cancel).
5557
#[error("We should cancel")]
56-
ErrorInShouldCancel(E),
58+
ErrorInShouldCancel(#[source] DP::Err),
5759

5860
/// Something unexpected happened.
5961
#[error("{0}")]
6062
Failure(String),
6163
}
64+
65+
impl<DP> std::fmt::Debug for PubGrubError<DP>
66+
where
67+
DP: DependencyProvider,
68+
{
69+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70+
match self {
71+
Self::NoSolution(arg0) => f.debug_tuple("NoSolution").field(arg0).finish(),
72+
Self::ErrorRetrievingDependencies {
73+
package,
74+
version,
75+
source,
76+
} => f
77+
.debug_struct("ErrorRetrievingDependencies")
78+
.field("package", package)
79+
.field("version", version)
80+
.field("source", source)
81+
.finish(),
82+
Self::SelfDependency { package, version } => f
83+
.debug_struct("SelfDependency")
84+
.field("package", package)
85+
.field("version", version)
86+
.finish(),
87+
Self::ErrorChoosingPackageVersion(arg0) => f
88+
.debug_tuple("ErrorChoosingPackageVersion")
89+
.field(arg0)
90+
.finish(),
91+
Self::ErrorInShouldCancel(arg0) => {
92+
f.debug_tuple("ErrorInShouldCancel").field(arg0).finish()
93+
}
94+
Self::Failure(arg0) => f.debug_tuple("Failure").field(arg0).finish(),
95+
}
96+
}
97+
}

src/internal/core.rs

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,57 @@
33
//! Core model and functions
44
//! to write a functional PubGrub algorithm.
55
6-
use std::error::Error;
6+
use std::collections::HashSet as Set;
77
use std::sync::Arc;
88

99
use crate::error::PubGrubError;
1010
use crate::internal::arena::Arena;
11-
use crate::internal::incompatibility::{IncompId, Incompatibility, Relation};
11+
use crate::internal::incompatibility::{Incompatibility, Relation};
1212
use crate::internal::partial_solution::SatisfierSearch::{
1313
DifferentDecisionLevels, SameDecisionLevels,
1414
};
1515
use crate::internal::partial_solution::{DecisionLevel, PartialSolution};
1616
use crate::internal::small_vec::SmallVec;
17-
use crate::package::Package;
1817
use crate::report::DerivationTree;
19-
use crate::type_aliases::{DependencyConstraints, Map, Set};
18+
use crate::solver::DependencyProvider;
19+
use crate::type_aliases::{DependencyConstraints, IncompDpId, Map};
2020
use crate::version_set::VersionSet;
2121

2222
/// Current state of the PubGrub algorithm.
2323
#[derive(Clone)]
24-
pub struct State<P: Package, VS: VersionSet, Priority: Ord + Clone> {
25-
root_package: P,
26-
root_version: VS::V,
24+
pub struct State<DP: DependencyProvider> {
25+
root_package: DP::P,
26+
root_version: DP::V,
2727

28-
incompatibilities: Map<P, Vec<IncompId<P, VS>>>,
28+
#[allow(clippy::type_complexity)]
29+
incompatibilities: Map<DP::P, Vec<IncompDpId<DP>>>,
2930

3031
/// Store the ids of incompatibilities that are already contradicted.
3132
/// For each one keep track of the decision level when it was found to be contradicted.
3233
/// These will stay contradicted until we have backtracked beyond its associated decision level.
33-
contradicted_incompatibilities: Map<IncompId<P, VS>, DecisionLevel>,
34+
contradicted_incompatibilities: Map<IncompDpId<DP>, DecisionLevel>,
3435

3536
/// All incompatibilities expressing dependencies,
3637
/// with common dependents merged.
37-
merged_dependencies: Map<(P, P), SmallVec<IncompId<P, VS>>>,
38+
#[allow(clippy::type_complexity)]
39+
merged_dependencies: Map<(DP::P, DP::P), SmallVec<IncompDpId<DP>>>,
3840

3941
/// Partial solution.
4042
/// TODO: remove pub.
41-
pub partial_solution: PartialSolution<P, VS, Priority>,
43+
pub partial_solution: PartialSolution<DP>,
4244

4345
/// The store is the reference storage for all incompatibilities.
44-
pub incompatibility_store: Arena<Incompatibility<P, VS>>,
46+
pub incompatibility_store: Arena<Incompatibility<DP::P, DP::VS>>,
4547

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

52-
impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
54+
impl<DP: DependencyProvider> State<DP> {
5355
/// Initialization of PubGrub state.
54-
pub fn init(root_package: P, root_version: VS::V) -> Self {
56+
pub fn init(root_package: DP::P, root_version: DP::V) -> Self {
5557
let mut incompatibility_store = Arena::new();
5658
let not_root_id = incompatibility_store.alloc(Incompatibility::not_root(
5759
root_package.clone(),
@@ -72,38 +74,38 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
7274
}
7375

7476
/// Add an incompatibility to the state.
75-
pub fn add_incompatibility(&mut self, incompat: Incompatibility<P, VS>) {
77+
pub fn add_incompatibility(&mut self, incompat: Incompatibility<DP::P, DP::VS>) {
7678
let id = self.incompatibility_store.alloc(incompat);
7779
self.merge_incompatibility(id);
7880
}
7981

8082
/// Add an incompatibility to the state.
8183
pub fn add_incompatibility_from_dependencies(
8284
&mut self,
83-
package: P,
84-
version: VS::V,
85-
deps: &DependencyConstraints<P, VS>,
86-
) -> std::ops::Range<IncompId<P, VS>> {
85+
package: DP::P,
86+
version: DP::V,
87+
deps: &DependencyConstraints<DP::P, DP::VS>,
88+
) -> std::ops::Range<IncompDpId<DP>> {
8789
// Create incompatibilities and allocate them in the store.
8890
let new_incompats_id_range =
8991
self.incompatibility_store
9092
.alloc_iter(deps.iter().map(|dep| {
9193
Incompatibility::from_dependency(
9294
package.clone(),
93-
VS::singleton(version.clone()),
95+
<DP::VS as VersionSet>::singleton(version.clone()),
9496
dep,
9597
)
9698
}));
9799
// Merge the newly created incompatibilities with the older ones.
98-
for id in IncompId::range_to_iter(new_incompats_id_range.clone()) {
100+
for id in IncompDpId::<DP>::range_to_iter(new_incompats_id_range.clone()) {
99101
self.merge_incompatibility(id);
100102
}
101103
new_incompats_id_range
102104
}
103105

104106
/// Unit propagation is the core mechanism of the solving algorithm.
105107
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
106-
pub fn unit_propagation<E: Error>(&mut self, package: P) -> Result<(), PubGrubError<P, VS, E>> {
108+
pub fn unit_propagation(&mut self, package: DP::P) -> Result<(), PubGrubError<DP>> {
107109
self.unit_propagation_buffer.clear();
108110
self.unit_propagation_buffer.push(package);
109111
while let Some(current_package) = self.unit_propagation_buffer.pop() {
@@ -179,10 +181,10 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
179181
/// Return the root cause and the backtracked model.
180182
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
181183
#[allow(clippy::type_complexity)]
182-
fn conflict_resolution<E: Error>(
184+
fn conflict_resolution(
183185
&mut self,
184-
incompatibility: IncompId<P, VS>,
185-
) -> Result<(P, IncompId<P, VS>), PubGrubError<P, VS, E>> {
186+
incompatibility: IncompDpId<DP>,
187+
) -> Result<(DP::P, IncompDpId<DP>), PubGrubError<DP>> {
186188
let mut current_incompat_id = incompatibility;
187189
let mut current_incompat_changed = false;
188190
loop {
@@ -229,7 +231,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
229231
/// Backtracking.
230232
fn backtrack(
231233
&mut self,
232-
incompat: IncompId<P, VS>,
234+
incompat: IncompDpId<DP>,
233235
incompat_changed: bool,
234236
decision_level: DecisionLevel,
235237
) {
@@ -257,7 +259,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
257259
/// (provided that no other version of foo exists between 1.0.0 and 2.0.0).
258260
/// We could collapse them into { foo (1.0.0 ∪ 1.1.0), not bar ^1.0.0 }
259261
/// without having to check the existence of other versions though.
260-
fn merge_incompatibility(&mut self, mut id: IncompId<P, VS>) {
262+
fn merge_incompatibility(&mut self, mut id: IncompDpId<DP>) {
261263
if let Some((p1, p2)) = self.incompatibility_store[id].as_dependency() {
262264
// If we are a dependency, there's a good chance we can be merged with a previous dependency
263265
let deps_lookup = self
@@ -295,8 +297,8 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
295297

296298
// Error reporting #########################################################
297299

298-
fn build_derivation_tree(&self, incompat: IncompId<P, VS>) -> DerivationTree<P, VS> {
299-
let mut all_ids = Set::default();
300+
fn build_derivation_tree(&self, incompat: IncompDpId<DP>) -> DerivationTree<DP::P, DP::VS> {
301+
let mut all_ids: Set<IncompDpId<DP>> = Set::default();
300302
let mut shared_ids = Set::default();
301303
let mut stack = vec![incompat];
302304
while let Some(i) = stack.pop() {

src/internal/incompatibility.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
161161
.unwrap()
162162
.unwrap_positive()
163163
.union(other.get(p1).unwrap().unwrap_positive()), // It is safe to `simplify` here
164-
(&p2, dep_term.map_or(&VS::empty(), |v| v.unwrap_negative())),
164+
(p2, dep_term.map_or(&VS::empty(), |v| v.unwrap_negative())),
165165
));
166166
}
167167

@@ -310,7 +310,6 @@ pub mod tests {
310310
use super::*;
311311
use crate::range::Range;
312312
use crate::term::tests::strategy as term_strat;
313-
use crate::type_aliases::Map;
314313
use proptest::prelude::*;
315314

316315
proptest! {

0 commit comments

Comments
 (0)