mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Upgrade PubGrub to dev branch (#147)
Updates to `29c48fb9f3daa11bd02794edd55060d0b01ee705` from the `pubgrub-rs` dev branch. This lets us reduce the number of changes we've made to PubGrub itself (now, only changing visibility to export a few things from the `solver.rs` module).
This commit is contained in:
parent
bcd281eb1f
commit
9b3405bf0e
36 changed files with 1416 additions and 865 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1967,6 +1967,7 @@ dependencies = [
|
||||||
name = "pubgrub"
|
name = "pubgrub"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use pubgrub::range::Range;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
|
@ -23,7 +24,7 @@ pub enum ResolveError {
|
||||||
Join(#[from] tokio::task::JoinError),
|
Join(#[from] tokio::task::JoinError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PubGrub(#[from] pubgrub::error::PubGrubError<PubGrubPackage, PubGrubVersion>),
|
PubGrub(#[from] pubgrub::error::PubGrubError<PubGrubPackage, Range<PubGrubVersion>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<futures::channel::mpsc::TrySendError<T>> for ResolveError {
|
impl<T> From<futures::channel::mpsc::TrySendError<T>> for ResolveError {
|
||||||
|
|
|
@ -58,19 +58,19 @@ pub(crate) fn version_range(
|
||||||
specifiers: Option<&pep508_rs::VersionOrUrl>,
|
specifiers: Option<&pep508_rs::VersionOrUrl>,
|
||||||
) -> Result<Range<PubGrubVersion>> {
|
) -> Result<Range<PubGrubVersion>> {
|
||||||
let Some(specifiers) = specifiers else {
|
let Some(specifiers) = specifiers else {
|
||||||
return Ok(Range::any());
|
return Ok(Range::full());
|
||||||
};
|
};
|
||||||
|
|
||||||
let pep508_rs::VersionOrUrl::VersionSpecifier(specifiers) = specifiers else {
|
let pep508_rs::VersionOrUrl::VersionSpecifier(specifiers) = specifiers else {
|
||||||
return Ok(Range::any());
|
return Ok(Range::full());
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut final_range = Range::any();
|
let mut final_range = Range::full();
|
||||||
for spec in specifiers.iter() {
|
for spec in specifiers.iter() {
|
||||||
let spec_range =
|
let spec_range =
|
||||||
PubGrubSpecifier::try_from(spec)?
|
PubGrubSpecifier::try_from(spec)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(Range::none(), |accum, range| {
|
.fold(Range::empty(), |accum, range| {
|
||||||
accum.union(&if range.end < *MAX_VERSION {
|
accum.union(&if range.end < *MAX_VERSION {
|
||||||
Range::between(range.start, range.end)
|
Range::between(range.start, range.end)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -37,6 +37,13 @@ impl pubgrub::version::Version for PubGrubVersion {
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// impl From<PubGrubVersion> for Range<PubGrubVersion> {
|
||||||
|
// fn from(value: PubGrubVersion) -> Self {
|
||||||
|
// Range::from(value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl<'a> From<&'a PubGrubVersion> for &'a pep440_rs::Version {
|
impl<'a> From<&'a PubGrubVersion> for &'a pep440_rs::Version {
|
||||||
fn from(version: &'a PubGrubVersion) -> Self {
|
fn from(version: &'a PubGrubVersion) -> Self {
|
||||||
&version.0
|
&version.0
|
||||||
|
|
|
@ -12,8 +12,8 @@ use futures::future::Either;
|
||||||
use futures::{pin_mut, FutureExt, StreamExt, TryFutureExt};
|
use futures::{pin_mut, FutureExt, StreamExt, TryFutureExt};
|
||||||
use pubgrub::error::PubGrubError;
|
use pubgrub::error::PubGrubError;
|
||||||
use pubgrub::range::Range;
|
use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{DependencyConstraints, Incompatibility, State};
|
use pubgrub::solver::{Incompatibility, State};
|
||||||
use pubgrub::type_aliases::SelectedDependencies;
|
use pubgrub::type_aliases::{DependencyConstraints, SelectedDependencies};
|
||||||
use tokio::select;
|
use tokio::select;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
use waitmap::WaitMap;
|
use waitmap::WaitMap;
|
||||||
|
@ -190,8 +190,9 @@ impl<'a> Resolver<'a> {
|
||||||
}
|
}
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
if let Some((dependent, _)) =
|
if let Some((dependent, _)) = constraints
|
||||||
constraints.iter().find(|(_, r)| r == &&Range::none())
|
.iter()
|
||||||
|
.find(|(_, r)| r == &&Range::<PubGrubVersion>::empty())
|
||||||
{
|
{
|
||||||
return Err(PubGrubError::DependencyOnTheEmptySet {
|
return Err(PubGrubError::DependencyOnTheEmptySet {
|
||||||
package: package.clone(),
|
package: package.clone(),
|
||||||
|
@ -378,7 +379,8 @@ impl<'a> Resolver<'a> {
|
||||||
) -> Result<Dependencies, ResolveError> {
|
) -> Result<Dependencies, ResolveError> {
|
||||||
match package {
|
match package {
|
||||||
PubGrubPackage::Root => {
|
PubGrubPackage::Root => {
|
||||||
let mut constraints = DependencyConstraints::default();
|
let mut constraints =
|
||||||
|
DependencyConstraints::<PubGrubPackage, Range<PubGrubVersion>>::default();
|
||||||
|
|
||||||
// Add the root requirements.
|
// Add the root requirements.
|
||||||
for (package, version) in
|
for (package, version) in
|
||||||
|
@ -420,7 +422,9 @@ impl<'a> Resolver<'a> {
|
||||||
let entry = self.cache.versions.wait(&file.hashes.sha256).await.unwrap();
|
let entry = self.cache.versions.wait(&file.hashes.sha256).await.unwrap();
|
||||||
let metadata = entry.value();
|
let metadata = entry.value();
|
||||||
|
|
||||||
let mut constraints = DependencyConstraints::default();
|
let mut constraints =
|
||||||
|
DependencyConstraints::<PubGrubPackage, Range<PubGrubVersion>>::default();
|
||||||
|
|
||||||
for (package, version) in
|
for (package, version) in
|
||||||
iter_requirements(metadata.requires_dist.iter(), extra.as_ref(), self.markers)
|
iter_requirements(metadata.requires_dist.iter(), extra.as_ref(), self.markers)
|
||||||
{
|
{
|
||||||
|
@ -465,7 +469,7 @@ impl<'a> Resolver<'a> {
|
||||||
}
|
}
|
||||||
constraints.insert(
|
constraints.insert(
|
||||||
PubGrubPackage::Package(package_name.clone(), None),
|
PubGrubPackage::Package(package_name.clone(), None),
|
||||||
Range::exact(version.clone()),
|
Range::singleton(version.clone()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,13 +563,13 @@ impl Default for SolverCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An enum used by [`DependencyProvider`] that holds information about package dependencies.
|
/// An enum used by [`DependencyProvider`] that holds information about package dependencies.
|
||||||
/// For each [Package] there is a [Range] of concrete versions it allows as a dependency.
|
/// For each [Package] there is a set of versions allowed as a dependency.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum Dependencies {
|
enum Dependencies {
|
||||||
/// Package dependencies are unavailable.
|
/// Package dependencies are unavailable.
|
||||||
Unknown,
|
Unknown,
|
||||||
/// Container for all available package versions.
|
/// Container for all available package versions.
|
||||||
Known(DependencyConstraints<PubGrubPackage, PubGrubVersion>),
|
Known(DependencyConstraints<PubGrubPackage, Range<PubGrubVersion>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
2
vendor/pubgrub/CHANGELOG.md
vendored
2
vendor/pubgrub/CHANGELOG.md
vendored
|
@ -48,7 +48,7 @@ The gist of it is:
|
||||||
|
|
||||||
#### Added
|
#### Added
|
||||||
|
|
||||||
- Links to code items in the code documenation.
|
- Links to code items in the code documentation.
|
||||||
- New `"serde"` feature that allows serializing some library types, useful for making simple reproducible bug reports.
|
- New `"serde"` feature that allows serializing some library types, useful for making simple reproducible bug reports.
|
||||||
- New variants for `error::PubGrubError` which are `DependencyOnTheEmptySet`,
|
- New variants for `error::PubGrubError` which are `DependencyOnTheEmptySet`,
|
||||||
`SelfDependency`, `ErrorChoosingPackageVersion` and `ErrorInShouldCancel`.
|
`SelfDependency`, `ErrorChoosingPackageVersion` and `ErrorInShouldCancel`.
|
||||||
|
|
2
vendor/pubgrub/Cargo.toml
vendored
2
vendor/pubgrub/Cargo.toml
vendored
|
@ -23,12 +23,14 @@ include = ["Cargo.toml", "LICENSE", "README.md", "src/**", "tests/**", "examples
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
|
log = "0.4.14" # for debug logs in tests
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
proptest = "0.10.1"
|
proptest = "0.10.1"
|
||||||
ron = "0.6"
|
ron = "0.6"
|
||||||
varisat = "0.2.2"
|
varisat = "0.2.2"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
env_logger = "0.9.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "large_case"
|
name = "large_case"
|
||||||
|
|
17
vendor/pubgrub/benches/large_case.rs
vendored
17
vendor/pubgrub/benches/large_case.rs
vendored
|
@ -5,16 +5,19 @@ extern crate criterion;
|
||||||
use self::criterion::*;
|
use self::criterion::*;
|
||||||
|
|
||||||
use pubgrub::package::Package;
|
use pubgrub::package::Package;
|
||||||
|
use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::{NumberVersion, SemanticVersion, Version};
|
use pubgrub::version::{NumberVersion, SemanticVersion};
|
||||||
|
use pubgrub::version_set::VersionSet;
|
||||||
use serde::de::Deserialize;
|
use serde::de::Deserialize;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
fn bench<'a, P: Package + Deserialize<'a>, V: Version + Hash + Deserialize<'a>>(
|
fn bench<'a, P: Package + Deserialize<'a>, VS: VersionSet + Deserialize<'a>>(
|
||||||
b: &mut Bencher,
|
b: &mut Bencher,
|
||||||
case: &'a str,
|
case: &'a str,
|
||||||
) {
|
) where
|
||||||
let dependency_provider: OfflineDependencyProvider<P, V> = ron::de::from_str(&case).unwrap();
|
<VS as VersionSet>::V: Deserialize<'a>,
|
||||||
|
{
|
||||||
|
let dependency_provider: OfflineDependencyProvider<P, VS> = ron::de::from_str(&case).unwrap();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
for p in dependency_provider.packages() {
|
for p in dependency_provider.packages() {
|
||||||
|
@ -35,11 +38,11 @@ fn bench_nested(c: &mut Criterion) {
|
||||||
let data = std::fs::read_to_string(&case).unwrap();
|
let data = std::fs::read_to_string(&case).unwrap();
|
||||||
if name.ends_with("u16_NumberVersion.ron") {
|
if name.ends_with("u16_NumberVersion.ron") {
|
||||||
group.bench_function(name, |b| {
|
group.bench_function(name, |b| {
|
||||||
bench::<u16, NumberVersion>(b, &data);
|
bench::<u16, Range<NumberVersion>>(b, &data);
|
||||||
});
|
});
|
||||||
} else if name.ends_with("str_SemanticVersion.ron") {
|
} else if name.ends_with("str_SemanticVersion.ron") {
|
||||||
group.bench_function(name, |b| {
|
group.bench_function(name, |b| {
|
||||||
bench::<&str, SemanticVersion>(b, &data);
|
bench::<&str, Range<SemanticVersion>>(b, &data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,51 +6,53 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::SemanticVersion;
|
use pubgrub::version::SemanticVersion;
|
||||||
|
|
||||||
|
type SemVS = Range<SemanticVersion>;
|
||||||
|
|
||||||
// https://github.com/dart-lang/pub/blob/master/doc/solver.md#branching-error-reporting
|
// https://github.com/dart-lang/pub/blob/master/doc/solver.md#branching-error-reporting
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// root 1.0.0 depends on foo ^1.0.0
|
// root 1.0.0 depends on foo ^1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", (1, 0, 0),
|
"root", (1, 0, 0),
|
||||||
vec![("foo", Range::between((1, 0, 0), (2, 0, 0)))],
|
[("foo", Range::from_range_bounds((1, 0, 0)..(2, 0, 0)))],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// foo 1.0.0 depends on a ^1.0.0 and b ^1.0.0
|
// foo 1.0.0 depends on a ^1.0.0 and b ^1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (1, 0, 0),
|
"foo", (1, 0, 0),
|
||||||
vec![
|
[
|
||||||
("a", Range::between((1, 0, 0), (2, 0, 0))),
|
("a", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))),
|
||||||
("b", Range::between((1, 0, 0), (2, 0, 0))),
|
("b", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// foo 1.1.0 depends on x ^1.0.0 and y ^1.0.0
|
// foo 1.1.0 depends on x ^1.0.0 and y ^1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (1, 1, 0),
|
"foo", (1, 1, 0),
|
||||||
vec![
|
[
|
||||||
("x", Range::between((1, 0, 0), (2, 0, 0))),
|
("x", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))),
|
||||||
("y", Range::between((1, 0, 0), (2, 0, 0))),
|
("y", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// a 1.0.0 depends on b ^2.0.0
|
// a 1.0.0 depends on b ^2.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"a", (1, 0, 0),
|
"a", (1, 0, 0),
|
||||||
vec![("b", Range::between((2, 0, 0), (3, 0, 0)))],
|
[("b", Range::from_range_bounds((2, 0, 0)..(3, 0, 0)))],
|
||||||
);
|
);
|
||||||
// b 1.0.0 and 2.0.0 have no dependencies.
|
// b 1.0.0 and 2.0.0 have no dependencies.
|
||||||
dependency_provider.add_dependencies("b", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("b", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("b", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("b", (2, 0, 0), []);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// x 1.0.0 depends on y ^2.0.0.
|
// x 1.0.0 depends on y ^2.0.0.
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"x", (1, 0, 0),
|
"x", (1, 0, 0),
|
||||||
vec![("y", Range::between((2, 0, 0), (3, 0, 0)))],
|
[("y", Range::from_range_bounds((2, 0, 0)..(3, 0, 0)))],
|
||||||
);
|
);
|
||||||
// y 1.0.0 and 2.0.0 have no dependencies.
|
// y 1.0.0 and 2.0.0 have no dependencies.
|
||||||
dependency_provider.add_dependencies("y", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("y", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("y", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("y", (2, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
||||||
|
|
|
@ -6,16 +6,21 @@ use std::error::Error;
|
||||||
use pubgrub::package::Package;
|
use pubgrub::package::Package;
|
||||||
use pubgrub::range::Range;
|
use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider};
|
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
|
// An example implementing caching dependency provider that will
|
||||||
// store queried dependencies in memory and check them before querying more from remote.
|
// 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,
|
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 {
|
pub fn new(remote_dependencies_provider: DP) -> Self {
|
||||||
CachingDependencyProvider {
|
CachingDependencyProvider {
|
||||||
remote_dependencies: remote_dependencies_provider,
|
remote_dependencies: remote_dependencies_provider,
|
||||||
|
@ -24,13 +29,13 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> CachingDependencyProv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P, V>
|
impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> DependencyProvider<P, VS>
|
||||||
for CachingDependencyProvider<P, V, DP>
|
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,
|
&self,
|
||||||
packages: impl Iterator<Item = (T, U)>,
|
packages: impl Iterator<Item = (T, U)>,
|
||||||
) -> Result<(T, Option<V>), Box<dyn Error>> {
|
) -> Result<(T, Option<VS::V>), Box<dyn Error + Send + Sync>> {
|
||||||
self.remote_dependencies.choose_package_version(packages)
|
self.remote_dependencies.choose_package_version(packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +43,8 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P,
|
||||||
fn get_dependencies(
|
fn get_dependencies(
|
||||||
&self,
|
&self,
|
||||||
package: &P,
|
package: &P,
|
||||||
version: &V,
|
version: &VS::V,
|
||||||
) -> Result<Dependencies<P, V>, Box<dyn Error>> {
|
) -> Result<Dependencies<P, VS>, Box<dyn Error + Send + Sync>> {
|
||||||
let mut cache = self.cached_dependencies.borrow_mut();
|
let mut cache = self.cached_dependencies.borrow_mut();
|
||||||
match cache.get_dependencies(package, version) {
|
match cache.get_dependencies(package, version) {
|
||||||
Ok(Dependencies::Unknown) => {
|
Ok(Dependencies::Unknown) => {
|
||||||
|
@ -49,7 +54,7 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P,
|
||||||
cache.add_dependencies(
|
cache.add_dependencies(
|
||||||
package.clone(),
|
package.clone(),
|
||||||
version.clone(),
|
version.clone(),
|
||||||
dependencies.clone().into_iter(),
|
dependencies.clone(),
|
||||||
);
|
);
|
||||||
Ok(Dependencies::Known(dependencies))
|
Ok(Dependencies::Known(dependencies))
|
||||||
}
|
}
|
||||||
|
@ -65,7 +70,7 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P,
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Simulating remote provider locally.
|
// 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.
|
// Add dependencies as needed. Here only root package is added.
|
||||||
remote_dependencies_provider.add_dependencies("root", 1, Vec::new());
|
remote_dependencies_provider.add_dependencies("root", 1, Vec::new());
|
||||||
|
|
12
vendor/pubgrub/examples/doc_interface.rs
vendored
12
vendor/pubgrub/examples/doc_interface.rs
vendored
|
@ -4,19 +4,21 @@ use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::NumberVersion;
|
use pubgrub::version::NumberVersion;
|
||||||
|
|
||||||
|
type NumVS = Range<NumberVersion>;
|
||||||
|
|
||||||
// `root` depends on `menu` and `icons`
|
// `root` depends on `menu` and `icons`
|
||||||
// `menu` depends on `dropdown`
|
// `menu` depends on `dropdown`
|
||||||
// `dropdown` depends on `icons`
|
// `dropdown` depends on `icons`
|
||||||
// `icons` has no dependency
|
// `icons` has no dependency
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<&str, NumVS>::new();
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", 1, vec![("menu", Range::any()), ("icons", Range::any())],
|
"root", 1, [("menu", Range::full()), ("icons", Range::full())],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("menu", 1, vec![("dropdown", Range::any())]);
|
dependency_provider.add_dependencies("menu", 1, [("dropdown", Range::full())]);
|
||||||
dependency_provider.add_dependencies("dropdown", 1, vec![("icons", Range::any())]);
|
dependency_provider.add_dependencies("dropdown", 1, [("icons", Range::full())]);
|
||||||
dependency_provider.add_dependencies("icons", 1, vec![]);
|
dependency_provider.add_dependencies("icons", 1, []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
let solution = resolve(&dependency_provider, "root", 1);
|
let solution = resolve(&dependency_provider, "root", 1);
|
||||||
|
|
66
vendor/pubgrub/examples/doc_interface_error.rs
vendored
66
vendor/pubgrub/examples/doc_interface_error.rs
vendored
|
@ -6,6 +6,8 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::SemanticVersion;
|
use pubgrub::version::SemanticVersion;
|
||||||
|
|
||||||
|
type SemVS = Range<SemanticVersion>;
|
||||||
|
|
||||||
// `root` depends on `menu`, `icons 1.0.0` and `intl 5.0.0`
|
// `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.0.0` depends on `dropdown < 2.0.0`
|
||||||
// `menu >= 1.1.0` depends on `dropdown >= 2.0.0`
|
// `menu >= 1.1.0` depends on `dropdown >= 2.0.0`
|
||||||
|
@ -15,59 +17,59 @@ use pubgrub::version::SemanticVersion;
|
||||||
// `intl` has no dependency
|
// `intl` has no dependency
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
// Direct dependencies: menu and icons.
|
// Direct dependencies: menu and icons.
|
||||||
dependency_provider.add_dependencies("root", (1, 0, 0), vec![
|
dependency_provider.add_dependencies("root", (1, 0, 0), [
|
||||||
("menu", Range::any()),
|
("menu", Range::full()),
|
||||||
("icons", Range::exact((1, 0, 0))),
|
("icons", Range::singleton((1, 0, 0))),
|
||||||
("intl", Range::exact((5, 0, 0))),
|
("intl", Range::singleton((5, 0, 0))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Dependencies of the menu lib.
|
// Dependencies of the menu lib.
|
||||||
dependency_provider.add_dependencies("menu", (1, 0, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 0, 0), [
|
||||||
("dropdown", Range::strictly_lower_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds(..(2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 1, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 1, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 2, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 2, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 3, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 3, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 4, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 4, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 5, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 5, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Dependencies of the dropdown lib.
|
// Dependencies of the dropdown lib.
|
||||||
dependency_provider.add_dependencies("dropdown", (1, 8, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (1, 8, 0), [
|
||||||
("intl", Range::exact((3, 0, 0))),
|
("intl", Range::singleton((3, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 0, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 0, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 1, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 1, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 2, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 2, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 3, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 3, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Icons have no dependencies.
|
// Icons have no dependencies.
|
||||||
dependency_provider.add_dependencies("icons", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("icons", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("icons", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("icons", (2, 0, 0), []);
|
||||||
|
|
||||||
// Intl have no dependencies.
|
// Intl have no dependencies.
|
||||||
dependency_provider.add_dependencies("intl", (3, 0, 0), vec![]);
|
dependency_provider.add_dependencies("intl", (3, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("intl", (4, 0, 0), vec![]);
|
dependency_provider.add_dependencies("intl", (4, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("intl", (5, 0, 0), vec![]);
|
dependency_provider.add_dependencies("intl", (5, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
||||||
|
|
|
@ -6,6 +6,8 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::SemanticVersion;
|
use pubgrub::version::SemanticVersion;
|
||||||
|
|
||||||
|
type SemVS = Range<SemanticVersion>;
|
||||||
|
|
||||||
// `root` depends on `menu` and `icons 1.0.0`
|
// `root` depends on `menu` and `icons 1.0.0`
|
||||||
// `menu 1.0.0` depends on `dropdown < 2.0.0`
|
// `menu 1.0.0` depends on `dropdown < 2.0.0`
|
||||||
// `menu >= 1.1.0` depends on `dropdown >= 2.0.0`
|
// `menu >= 1.1.0` depends on `dropdown >= 2.0.0`
|
||||||
|
@ -14,51 +16,51 @@ use pubgrub::version::SemanticVersion;
|
||||||
// `icons` has no dependency
|
// `icons` has no dependency
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
// Direct dependencies: menu and icons.
|
// Direct dependencies: menu and icons.
|
||||||
dependency_provider.add_dependencies("root", (1, 0, 0), vec![
|
dependency_provider.add_dependencies("root", (1, 0, 0), [
|
||||||
("menu", Range::any()),
|
("menu", Range::full()),
|
||||||
("icons", Range::exact((1, 0, 0))),
|
("icons", Range::singleton((1, 0, 0))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Dependencies of the menu lib.
|
// Dependencies of the menu lib.
|
||||||
dependency_provider.add_dependencies("menu", (1, 0, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 0, 0), [
|
||||||
("dropdown", Range::strictly_lower_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds(..(2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 1, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 1, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 2, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 2, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 3, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 3, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 4, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 4, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("menu", (1, 5, 0), vec![
|
dependency_provider.add_dependencies("menu", (1, 5, 0), [
|
||||||
("dropdown", Range::higher_than((2, 0, 0))),
|
("dropdown", Range::from_range_bounds((2, 0, 0)..)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Dependencies of the dropdown lib.
|
// Dependencies of the dropdown lib.
|
||||||
dependency_provider.add_dependencies("dropdown", (1, 8, 0), vec![]);
|
dependency_provider.add_dependencies("dropdown", (1, 8, 0), []);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 0, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 0, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 1, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 1, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 2, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 2, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
dependency_provider.add_dependencies("dropdown", (2, 3, 0), vec![
|
dependency_provider.add_dependencies("dropdown", (2, 3, 0), [
|
||||||
("icons", Range::exact((2, 0, 0))),
|
("icons", Range::singleton((2, 0, 0))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Icons has no dependency.
|
// Icons has no dependency.
|
||||||
dependency_provider.add_dependencies("icons", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("icons", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("icons", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("icons", (2, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
||||||
|
|
|
@ -6,33 +6,35 @@ use pubgrub::report::{DefaultStringReporter, Reporter};
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::SemanticVersion;
|
use pubgrub::version::SemanticVersion;
|
||||||
|
|
||||||
|
type SemVS = Range<SemanticVersion>;
|
||||||
|
|
||||||
// https://github.com/dart-lang/pub/blob/master/doc/solver.md#linear-error-reporting
|
// https://github.com/dart-lang/pub/blob/master/doc/solver.md#linear-error-reporting
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// root 1.0.0 depends on foo ^1.0.0 and baz ^1.0.0
|
// root 1.0.0 depends on foo ^1.0.0 and baz ^1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", (1, 0, 0),
|
"root", (1, 0, 0),
|
||||||
vec![
|
[
|
||||||
("foo", Range::between((1, 0, 0), (2, 0, 0))),
|
("foo", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))),
|
||||||
("baz", Range::between((1, 0, 0), (2, 0, 0))),
|
("baz", Range::from_range_bounds((1, 0, 0)..(2, 0, 0))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// foo 1.0.0 depends on bar ^2.0.0
|
// foo 1.0.0 depends on bar ^2.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (1, 0, 0),
|
"foo", (1, 0, 0),
|
||||||
vec![("bar", Range::between((2, 0, 0), (3, 0, 0)))],
|
[("bar", Range::from_range_bounds((2, 0, 0)..(3, 0, 0)))],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// bar 2.0.0 depends on baz ^3.0.0
|
// bar 2.0.0 depends on baz ^3.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"bar", (2, 0, 0),
|
"bar", (2, 0, 0),
|
||||||
vec![("baz", Range::between((3, 0, 0), (4, 0, 0)))],
|
[("baz", Range::from_range_bounds((3, 0, 0)..(4, 0, 0)))],
|
||||||
);
|
);
|
||||||
// baz 1.0.0 and 3.0.0 have no dependencies
|
// baz 1.0.0 and 3.0.0 have no dependencies
|
||||||
dependency_provider.add_dependencies("baz", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("baz", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("baz", (3, 0, 0), vec![]);
|
dependency_provider.add_dependencies("baz", (3, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
match resolve(&dependency_provider, "root", (1, 0, 0)) {
|
||||||
|
|
28
vendor/pubgrub/release.md
vendored
Normal file
28
vendor/pubgrub/release.md
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Creation of a new release
|
||||||
|
|
||||||
|
This is taking the 0.2.1 release as an example.
|
||||||
|
|
||||||
|
## GitHub stuff
|
||||||
|
|
||||||
|
- Checkout the prep-v0.2.1 branch
|
||||||
|
- Update the release date in the changelog and push to the PR.
|
||||||
|
- Squash merge the PR to the dev branch
|
||||||
|
- Check that the merged PR is passing the tests on the dev branch
|
||||||
|
- Pull the updated dev locally
|
||||||
|
- Switch to the release branch
|
||||||
|
- Merge locally dev into release in fast-forward mode, we want to keep the history of commits and the merge point.
|
||||||
|
- `git tag -a v0.2.1 -m "v0.2.1: mostly perf improvements"`
|
||||||
|
- (Optional) cryptographically sign the tag
|
||||||
|
- On GitHub, edit the branch protection setting for release: uncheck include admin, and save
|
||||||
|
- Push release to github: git push --follow-tags
|
||||||
|
- Reset the release branch protection to include admins
|
||||||
|
- On GitHub, create a release from that tag.
|
||||||
|
|
||||||
|
## Crates.io stuff
|
||||||
|
|
||||||
|
- `cargo publish --dry-run`
|
||||||
|
- `cargo publish`
|
||||||
|
|
||||||
|
## Community stuff
|
||||||
|
|
||||||
|
Talk about the awesome new features of the new release online.
|
12
vendor/pubgrub/src/error.rs
vendored
12
vendor/pubgrub/src/error.rs
vendored
|
@ -6,14 +6,14 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::package::Package;
|
use crate::package::Package;
|
||||||
use crate::report::DerivationTree;
|
use crate::report::DerivationTree;
|
||||||
use crate::version::Version;
|
use crate::version_set::VersionSet;
|
||||||
|
|
||||||
/// Errors that may occur while solving dependencies.
|
/// Errors that may occur while solving dependencies.
|
||||||
#[derive(Error, Debug)]
|
#[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.
|
/// There is no solution for this set of dependencies.
|
||||||
#[error("No solution")]
|
#[error("No solution")]
|
||||||
NoSolution(DerivationTree<P, V>),
|
NoSolution(DerivationTree<P, VS>),
|
||||||
|
|
||||||
/// Error arising when the implementer of
|
/// Error arising when the implementer of
|
||||||
/// [DependencyProvider](crate::solver::DependencyProvider)
|
/// [DependencyProvider](crate::solver::DependencyProvider)
|
||||||
|
@ -24,7 +24,7 @@ pub enum PubGrubError<P: Package, V: Version> {
|
||||||
/// Package whose dependencies we want.
|
/// Package whose dependencies we want.
|
||||||
package: P,
|
package: P,
|
||||||
/// Version of the package for which we want the dependencies.
|
/// Version of the package for which we want the dependencies.
|
||||||
version: V,
|
version: VS::V,
|
||||||
/// Error raised by the implementer of
|
/// Error raised by the implementer of
|
||||||
/// [DependencyProvider](crate::solver::DependencyProvider).
|
/// [DependencyProvider](crate::solver::DependencyProvider).
|
||||||
source: Box<dyn std::error::Error + Send + Sync>,
|
source: Box<dyn std::error::Error + Send + Sync>,
|
||||||
|
@ -40,7 +40,7 @@ pub enum PubGrubError<P: Package, V: Version> {
|
||||||
/// Package whose dependencies we want.
|
/// Package whose dependencies we want.
|
||||||
package: P,
|
package: P,
|
||||||
/// Version of the package for which we want the dependencies.
|
/// 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.
|
/// The dependent package that requires us to pick from the empty set.
|
||||||
dependent: P,
|
dependent: P,
|
||||||
},
|
},
|
||||||
|
@ -55,7 +55,7 @@ pub enum PubGrubError<P: Package, V: Version> {
|
||||||
/// Package whose dependencies we want.
|
/// Package whose dependencies we want.
|
||||||
package: P,
|
package: P,
|
||||||
/// Version of the package for which we want the dependencies.
|
/// Version of the package for which we want the dependencies.
|
||||||
version: V,
|
version: VS::V,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Error arising when the implementer of
|
/// Error arising when the implementer of
|
||||||
|
|
2
vendor/pubgrub/src/internal/arena.rs
vendored
2
vendor/pubgrub/src/internal/arena.rs
vendored
|
@ -55,7 +55,7 @@ impl<T> Id<T> {
|
||||||
}
|
}
|
||||||
fn from(n: u32) -> Self {
|
fn from(n: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw: n as u32,
|
raw: n,
|
||||||
_ty: PhantomData,
|
_ty: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
vendor/pubgrub/src/internal/core.rs
vendored
51
vendor/pubgrub/src/internal/core.rs
vendored
|
@ -15,28 +15,27 @@ use crate::internal::partial_solution::{DecisionLevel, PartialSolution};
|
||||||
use crate::internal::small_vec::SmallVec;
|
use crate::internal::small_vec::SmallVec;
|
||||||
use crate::package::Package;
|
use crate::package::Package;
|
||||||
use crate::report::DerivationTree;
|
use crate::report::DerivationTree;
|
||||||
use crate::solver::DependencyConstraints;
|
use crate::type_aliases::{DependencyConstraints, Map};
|
||||||
use crate::type_aliases::Map;
|
use crate::version_set::VersionSet;
|
||||||
use crate::version::Version;
|
|
||||||
|
|
||||||
/// Current state of the PubGrub algorithm.
|
/// Current state of the PubGrub algorithm.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct State<P: Package, V: Version> {
|
pub struct State<P: Package, VS: VersionSet> {
|
||||||
root_package: P,
|
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
|
/// Store the ids of incompatibilities that are already contradicted
|
||||||
/// and will stay that way until the next conflict and backtrack is operated.
|
/// 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.
|
/// Partial solution.
|
||||||
/// TODO: remove pub.
|
/// TODO: remove pub.
|
||||||
pub partial_solution: PartialSolution<P, V>,
|
pub partial_solution: PartialSolution<P, VS>,
|
||||||
|
|
||||||
/// The store is the reference storage for all incompatibilities.
|
/// 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`.
|
/// This is a stack of work to be done in `unit_propagation`.
|
||||||
/// It can definitely be a local variable to that method, but
|
/// It can definitely be a local variable to that method, but
|
||||||
|
@ -44,9 +43,9 @@ pub struct State<P: Package, V: Version> {
|
||||||
unit_propagation_buffer: SmallVec<P>,
|
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.
|
/// 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 mut incompatibility_store = Arena::new();
|
||||||
let not_root_id = incompatibility_store.alloc(Incompatibility::not_root(
|
let not_root_id = incompatibility_store.alloc(Incompatibility::not_root(
|
||||||
root_package.clone(),
|
root_package.clone(),
|
||||||
|
@ -66,7 +65,7 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an incompatibility to the state.
|
/// 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);
|
let id = self.incompatibility_store.alloc(incompat);
|
||||||
self.merge_incompatibility(id);
|
self.merge_incompatibility(id);
|
||||||
}
|
}
|
||||||
|
@ -75,9 +74,9 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
pub fn add_incompatibility_from_dependencies(
|
pub fn add_incompatibility_from_dependencies(
|
||||||
&mut self,
|
&mut self,
|
||||||
package: P,
|
package: P,
|
||||||
version: V,
|
version: VS::V,
|
||||||
deps: &DependencyConstraints<P, V>,
|
deps: &DependencyConstraints<P, VS>,
|
||||||
) -> std::ops::Range<IncompId<P, V>> {
|
) -> std::ops::Range<IncompId<P, VS>> {
|
||||||
// Create incompatibilities and allocate them in the store.
|
// Create incompatibilities and allocate them in the store.
|
||||||
let new_incompats_id_range = self
|
let new_incompats_id_range = self
|
||||||
.incompatibility_store
|
.incompatibility_store
|
||||||
|
@ -92,13 +91,13 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if an incompatibility is terminal.
|
/// 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)
|
incompatibility.is_terminal(&self.root_package, &self.root_version)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unit propagation is the core mechanism of the solving algorithm.
|
/// Unit propagation is the core mechanism of the solving algorithm.
|
||||||
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
|
/// 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.clear();
|
||||||
self.unit_propagation_buffer.push(package);
|
self.unit_propagation_buffer.push(package);
|
||||||
while let Some(current_package) = self.unit_propagation_buffer.pop() {
|
while let Some(current_package) = self.unit_propagation_buffer.pop() {
|
||||||
|
@ -115,6 +114,10 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
// If the partial solution satisfies the incompatibility
|
// If the partial solution satisfies the incompatibility
|
||||||
// we must perform conflict resolution.
|
// we must perform conflict resolution.
|
||||||
Relation::Satisfied => {
|
Relation::Satisfied => {
|
||||||
|
log::info!(
|
||||||
|
"Start conflict resolution because incompat satisfied:\n {}",
|
||||||
|
current_incompat
|
||||||
|
);
|
||||||
conflict_id = Some(incompat_id);
|
conflict_id = Some(incompat_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -158,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>
|
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
|
||||||
fn conflict_resolution(
|
fn conflict_resolution(
|
||||||
&mut self,
|
&mut self,
|
||||||
incompatibility: IncompId<P, V>,
|
incompatibility: IncompId<P, VS>,
|
||||||
) -> Result<(P, IncompId<P, V>), PubGrubError<P, V>> {
|
) -> Result<(P, IncompId<P, VS>), PubGrubError<P, VS>> {
|
||||||
let mut current_incompat_id = incompatibility;
|
let mut current_incompat_id = incompatibility;
|
||||||
let mut current_incompat_changed = false;
|
let mut current_incompat_changed = false;
|
||||||
loop {
|
loop {
|
||||||
|
@ -183,6 +186,7 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
current_incompat_changed,
|
current_incompat_changed,
|
||||||
previous_satisfier_level,
|
previous_satisfier_level,
|
||||||
);
|
);
|
||||||
|
log::info!("backtrack to {:?}", previous_satisfier_level);
|
||||||
return Ok((package, current_incompat_id));
|
return Ok((package, current_incompat_id));
|
||||||
}
|
}
|
||||||
SameDecisionLevels { satisfier_cause } => {
|
SameDecisionLevels { satisfier_cause } => {
|
||||||
|
@ -192,6 +196,7 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
&package,
|
&package,
|
||||||
&self.incompatibility_store,
|
&self.incompatibility_store,
|
||||||
);
|
);
|
||||||
|
log::info!("prior cause: {}", prior_cause);
|
||||||
current_incompat_id = self.incompatibility_store.alloc(prior_cause);
|
current_incompat_id = self.incompatibility_store.alloc(prior_cause);
|
||||||
current_incompat_changed = true;
|
current_incompat_changed = true;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +208,7 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
/// Backtracking.
|
/// Backtracking.
|
||||||
fn backtrack(
|
fn backtrack(
|
||||||
&mut self,
|
&mut self,
|
||||||
incompat: IncompId<P, V>,
|
incompat: IncompId<P, VS>,
|
||||||
incompat_changed: bool,
|
incompat_changed: bool,
|
||||||
decision_level: DecisionLevel,
|
decision_level: DecisionLevel,
|
||||||
) {
|
) {
|
||||||
|
@ -234,7 +239,7 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
/// Here we do the simple stupid thing of just growing the Vec.
|
/// Here we do the simple stupid thing of just growing the Vec.
|
||||||
/// It may not be trivial since those incompatibilities
|
/// It may not be trivial since those incompatibilities
|
||||||
/// may already have derived others.
|
/// 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() {
|
for (pkg, _term) in self.incompatibility_store[id].iter() {
|
||||||
self.incompatibilities
|
self.incompatibilities
|
||||||
.entry(pkg.clone())
|
.entry(pkg.clone())
|
||||||
|
@ -245,12 +250,12 @@ impl<P: Package, V: Version> State<P, V> {
|
||||||
|
|
||||||
// Error reporting #########################################################
|
// 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);
|
let shared_ids = self.find_shared_ids(incompat);
|
||||||
Incompatibility::build_derivation_tree(incompat, &shared_ids, &self.incompatibility_store)
|
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 all_ids = Set::new();
|
||||||
let mut shared_ids = Set::new();
|
let mut shared_ids = Set::new();
|
||||||
let mut stack = vec![incompat];
|
let mut stack = vec![incompat];
|
||||||
|
|
94
vendor/pubgrub/src/internal/incompatibility.rs
vendored
94
vendor/pubgrub/src/internal/incompatibility.rs
vendored
|
@ -9,10 +9,9 @@ use std::fmt;
|
||||||
use crate::internal::arena::{Arena, Id};
|
use crate::internal::arena::{Arena, Id};
|
||||||
use crate::internal::small_map::SmallMap;
|
use crate::internal::small_map::SmallMap;
|
||||||
use crate::package::Package;
|
use crate::package::Package;
|
||||||
use crate::range::Range;
|
|
||||||
use crate::report::{DefaultStringReporter, DerivationTree, Derived, External};
|
use crate::report::{DefaultStringReporter, DerivationTree, Derived, External};
|
||||||
use crate::term::{self, Term};
|
use crate::term::{self, Term};
|
||||||
use crate::version::Version;
|
use crate::version_set::VersionSet;
|
||||||
|
|
||||||
/// An incompatibility is a set of terms for different packages
|
/// An incompatibility is a set of terms for different packages
|
||||||
/// that should never be satisfied all together.
|
/// that should never be satisfied all together.
|
||||||
|
@ -30,26 +29,26 @@ use crate::version::Version;
|
||||||
/// during conflict resolution. More about all this in
|
/// during conflict resolution. More about all this in
|
||||||
/// [PubGrub documentation](https://github.com/dart-lang/pub/blob/master/doc/solver.md#incompatibility).
|
/// [PubGrub documentation](https://github.com/dart-lang/pub/blob/master/doc/solver.md#incompatibility).
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Incompatibility<P: Package, V: Version> {
|
pub struct Incompatibility<P: Package, VS: VersionSet> {
|
||||||
package_terms: SmallMap<P, Term<V>>,
|
package_terms: SmallMap<P, Term<VS>>,
|
||||||
kind: Kind<P, V>,
|
kind: Kind<P, VS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type alias of unique identifiers for incompatibilities.
|
/// Type alias of unique identifiers for incompatibilities.
|
||||||
pub(crate) type IncompId<P, V> = Id<Incompatibility<P, V>>;
|
pub type IncompId<P, VS> = Id<Incompatibility<P, VS>>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Kind<P: Package, V: Version> {
|
enum Kind<P: Package, VS: VersionSet> {
|
||||||
/// Initial incompatibility aiming at picking the root package for the first decision.
|
/// Initial incompatibility aiming at picking the root package for the first decision.
|
||||||
NotRoot(P, V),
|
NotRoot(P, VS::V),
|
||||||
/// There are no versions in the given range for this package.
|
/// There are no versions in the given range for this package.
|
||||||
NoVersions(P, Range<V>),
|
NoVersions(P, VS),
|
||||||
/// Dependencies of the package are unavailable for versions in that range.
|
/// Dependencies of the package are unavailable for versions in that range.
|
||||||
UnavailableDependencies(P, Range<V>),
|
UnavailableDependencies(P, VS),
|
||||||
/// Incompatibility coming from the dependencies of a given package.
|
/// Incompatibility coming from the dependencies of a given package.
|
||||||
FromDependencyOf(P, Range<V>, P, Range<V>),
|
FromDependencyOf(P, VS, P, VS),
|
||||||
/// Derived from two causes. Stores cause ids.
|
/// Derived from two causes. Stores cause ids.
|
||||||
DerivedFrom(IncompId<P, V>, IncompId<P, V>),
|
DerivedFrom(IncompId<P, VS>, IncompId<P, VS>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Relation describes how a set of terms can be compared to an incompatibility.
|
/// A Relation describes how a set of terms can be compared to an incompatibility.
|
||||||
|
@ -69,52 +68,52 @@ pub enum Relation<P: Package> {
|
||||||
Inconclusive,
|
Inconclusive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> Incompatibility<P, V> {
|
impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
|
||||||
/// Create the initial "not Root" incompatibility.
|
/// Create the initial "not Root" incompatibility.
|
||||||
pub fn not_root(package: P, version: V) -> Self {
|
pub fn not_root(package: P, version: VS::V) -> Self {
|
||||||
Self {
|
Self {
|
||||||
package_terms: SmallMap::One([(
|
package_terms: SmallMap::One([(
|
||||||
package.clone(),
|
package.clone(),
|
||||||
Term::Negative(Range::exact(version.clone())),
|
Term::Negative(VS::singleton(version.clone())),
|
||||||
)]),
|
)]),
|
||||||
kind: Kind::NotRoot(package, version),
|
kind: Kind::NotRoot(package, version),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an incompatibility to remember
|
/// Create an incompatibility to remember
|
||||||
/// that a given range does not contain any version.
|
/// that a given set does not contain any version.
|
||||||
pub fn no_versions(package: P, term: Term<V>) -> Self {
|
pub fn no_versions(package: P, term: Term<VS>) -> Self {
|
||||||
let range = match &term {
|
let set = match &term {
|
||||||
Term::Positive(r) => r.clone(),
|
Term::Positive(r) => r.clone(),
|
||||||
Term::Negative(_) => panic!("No version should have a positive term"),
|
Term::Negative(_) => panic!("No version should have a positive term"),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
package_terms: SmallMap::One([(package.clone(), term)]),
|
package_terms: SmallMap::One([(package.clone(), term)]),
|
||||||
kind: Kind::NoVersions(package, range),
|
kind: Kind::NoVersions(package, set),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an incompatibility to remember
|
/// Create an incompatibility to remember
|
||||||
/// that a package version is not selectable
|
/// that a package version is not selectable
|
||||||
/// because its list of dependencies is unavailable.
|
/// because its list of dependencies is unavailable.
|
||||||
pub fn unavailable_dependencies(package: P, version: V) -> Self {
|
pub fn unavailable_dependencies(package: P, version: VS::V) -> Self {
|
||||||
let range = Range::exact(version);
|
let set = VS::singleton(version);
|
||||||
Self {
|
Self {
|
||||||
package_terms: SmallMap::One([(package.clone(), Term::Positive(range.clone()))]),
|
package_terms: SmallMap::One([(package.clone(), Term::Positive(set.clone()))]),
|
||||||
kind: Kind::UnavailableDependencies(package, range),
|
kind: Kind::UnavailableDependencies(package, set),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build an incompatibility from a given dependency.
|
/// Build an incompatibility from a given dependency.
|
||||||
pub fn from_dependency(package: P, version: V, dep: (&P, &Range<V>)) -> Self {
|
pub fn from_dependency(package: P, version: VS::V, dep: (&P, &VS)) -> Self {
|
||||||
let range1 = Range::exact(version);
|
let set1 = VS::singleton(version);
|
||||||
let (p2, range2) = dep;
|
let (p2, set2) = dep;
|
||||||
Self {
|
Self {
|
||||||
package_terms: SmallMap::Two([
|
package_terms: SmallMap::Two([
|
||||||
(package.clone(), Term::Positive(range1.clone())),
|
(package.clone(), Term::Positive(set1.clone())),
|
||||||
(p2.clone(), Term::Negative(range2.clone())),
|
(p2.clone(), Term::Negative(set2.clone())),
|
||||||
]),
|
]),
|
||||||
kind: Kind::FromDependencyOf(package, range1, p2.clone(), range2.clone()),
|
kind: Kind::FromDependencyOf(package, set1, p2.clone(), set2.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,24 +144,24 @@ impl<P: Package, V: Version> Incompatibility<P, V> {
|
||||||
|
|
||||||
/// Check if an incompatibility should mark the end of the algorithm
|
/// Check if an incompatibility should mark the end of the algorithm
|
||||||
/// because it satisfies the root package.
|
/// because it satisfies the root package.
|
||||||
pub fn is_terminal(&self, root_package: &P, root_version: &V) -> bool {
|
pub fn is_terminal(&self, root_package: &P, root_version: &VS::V) -> bool {
|
||||||
if self.package_terms.len() == 0 {
|
if self.package_terms.len() == 0 {
|
||||||
true
|
true
|
||||||
} else if self.package_terms.len() > 1 {
|
} else if self.package_terms.len() > 1 {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let (package, term) = self.package_terms.iter().next().unwrap();
|
let (package, term) = self.package_terms.iter().next().unwrap();
|
||||||
(package == root_package) && term.contains(&root_version)
|
(package == root_package) && term.contains(root_version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the term related to a given package (if it exists).
|
/// Get the term related to a given package (if it exists).
|
||||||
pub fn get(&self, package: &P) -> Option<&Term<V>> {
|
pub fn get(&self, package: &P) -> Option<&Term<VS>> {
|
||||||
self.package_terms.get(package)
|
self.package_terms.get(package)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over packages.
|
/// Iterate over packages.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&P, &Term<V>)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&P, &Term<VS>)> {
|
||||||
self.package_terms.iter()
|
self.package_terms.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +180,7 @@ impl<P: Package, V: Version> Incompatibility<P, V> {
|
||||||
self_id: Id<Self>,
|
self_id: Id<Self>,
|
||||||
shared_ids: &Set<Id<Self>>,
|
shared_ids: &Set<Id<Self>>,
|
||||||
store: &Arena<Self>,
|
store: &Arena<Self>,
|
||||||
) -> DerivationTree<P, V> {
|
) -> DerivationTree<P, VS> {
|
||||||
match &store[self_id].kind {
|
match &store[self_id].kind {
|
||||||
Kind::DerivedFrom(id1, id2) => {
|
Kind::DerivedFrom(id1, id2) => {
|
||||||
let cause1 = Self::build_derivation_tree(*id1, shared_ids, store);
|
let cause1 = Self::build_derivation_tree(*id1, shared_ids, store);
|
||||||
|
@ -197,30 +196,30 @@ impl<P: Package, V: Version> Incompatibility<P, V> {
|
||||||
Kind::NotRoot(package, version) => {
|
Kind::NotRoot(package, version) => {
|
||||||
DerivationTree::External(External::NotRoot(package.clone(), version.clone()))
|
DerivationTree::External(External::NotRoot(package.clone(), version.clone()))
|
||||||
}
|
}
|
||||||
Kind::NoVersions(package, range) => {
|
Kind::NoVersions(package, set) => {
|
||||||
DerivationTree::External(External::NoVersions(package.clone(), range.clone()))
|
DerivationTree::External(External::NoVersions(package.clone(), set.clone()))
|
||||||
}
|
}
|
||||||
Kind::UnavailableDependencies(package, range) => DerivationTree::External(
|
Kind::UnavailableDependencies(package, set) => DerivationTree::External(
|
||||||
External::UnavailableDependencies(package.clone(), range.clone()),
|
External::UnavailableDependencies(package.clone(), set.clone()),
|
||||||
),
|
),
|
||||||
Kind::FromDependencyOf(package, range, dep_package, dep_range) => {
|
Kind::FromDependencyOf(package, set, dep_package, dep_set) => {
|
||||||
DerivationTree::External(External::FromDependencyOf(
|
DerivationTree::External(External::FromDependencyOf(
|
||||||
package.clone(),
|
package.clone(),
|
||||||
range.clone(),
|
set.clone(),
|
||||||
dep_package.clone(),
|
dep_package.clone(),
|
||||||
dep_range.clone(),
|
dep_set.clone(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P: Package, V: Version + 'a> Incompatibility<P, V> {
|
impl<'a, P: Package, VS: VersionSet + 'a> Incompatibility<P, VS> {
|
||||||
/// CF definition of Relation enum.
|
/// CF definition of Relation enum.
|
||||||
pub fn relation(&self, terms: impl Fn(&P) -> Option<&'a Term<V>>) -> Relation<P> {
|
pub fn relation(&self, terms: impl Fn(&P) -> Option<&'a Term<VS>>) -> Relation<P> {
|
||||||
let mut relation = Relation::Satisfied;
|
let mut relation = Relation::Satisfied;
|
||||||
for (package, incompat_term) in self.package_terms.iter() {
|
for (package, incompat_term) in self.package_terms.iter() {
|
||||||
match terms(package).map(|term| incompat_term.relation_with(&term)) {
|
match terms(package).map(|term| incompat_term.relation_with(term)) {
|
||||||
Some(term::Relation::Satisfied) => {}
|
Some(term::Relation::Satisfied) => {}
|
||||||
Some(term::Relation::Contradicted) => {
|
Some(term::Relation::Contradicted) => {
|
||||||
return Relation::Contradicted(package.clone());
|
return Relation::Contradicted(package.clone());
|
||||||
|
@ -243,7 +242,7 @@ impl<'a, P: Package, V: Version + 'a> Incompatibility<P, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> fmt::Display for Incompatibility<P, V> {
|
impl<P: Package, VS: VersionSet> fmt::Display for Incompatibility<P, VS> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -258,6 +257,7 @@ impl<P: Package, V: Version> fmt::Display for Incompatibility<P, V> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::range::Range;
|
||||||
use crate::term::tests::strategy as term_strat;
|
use crate::term::tests::strategy as term_strat;
|
||||||
use crate::type_aliases::Map;
|
use crate::type_aliases::Map;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
@ -276,12 +276,12 @@ pub mod tests {
|
||||||
let mut store = Arena::new();
|
let mut store = Arena::new();
|
||||||
let i1 = store.alloc(Incompatibility {
|
let i1 = store.alloc(Incompatibility {
|
||||||
package_terms: SmallMap::Two([("p1", t1.clone()), ("p2", t2.negate())]),
|
package_terms: SmallMap::Two([("p1", t1.clone()), ("p2", t2.negate())]),
|
||||||
kind: Kind::UnavailableDependencies("0", Range::any())
|
kind: Kind::UnavailableDependencies("0", Range::full())
|
||||||
});
|
});
|
||||||
|
|
||||||
let i2 = store.alloc(Incompatibility {
|
let i2 = store.alloc(Incompatibility {
|
||||||
package_terms: SmallMap::Two([("p2", t2), ("p3", t3.clone())]),
|
package_terms: SmallMap::Two([("p2", t2), ("p3", t3.clone())]),
|
||||||
kind: Kind::UnavailableDependencies("0", Range::any())
|
kind: Kind::UnavailableDependencies("0", Range::full())
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut i3 = Map::default();
|
let mut i3 = Map::default();
|
||||||
|
|
12
vendor/pubgrub/src/internal/mod.rs
vendored
12
vendor/pubgrub/src/internal/mod.rs
vendored
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
//! Non exposed modules.
|
//! Non exposed modules.
|
||||||
|
|
||||||
pub(crate) mod arena;
|
pub mod arena;
|
||||||
pub(crate) mod core;
|
pub mod core;
|
||||||
pub(crate) mod incompatibility;
|
pub mod incompatibility;
|
||||||
pub(crate) mod partial_solution;
|
pub mod partial_solution;
|
||||||
pub(crate) mod small_map;
|
pub mod small_map;
|
||||||
pub(crate) mod small_vec;
|
pub mod small_vec;
|
||||||
|
|
162
vendor/pubgrub/src/internal/partial_solution.rs
vendored
162
vendor/pubgrub/src/internal/partial_solution.rs
vendored
|
@ -1,16 +1,17 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
//! A Memory acts like a structured partial solution
|
//! A Memory acts like a structured partial solution
|
||||||
//! where terms are regrouped by package in a [Map](crate::type_aliases::Map).
|
//! where terms are regrouped by package in a [Map].
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::internal::arena::Arena;
|
use crate::internal::arena::Arena;
|
||||||
use crate::internal::incompatibility::{IncompId, Incompatibility, Relation};
|
use crate::internal::incompatibility::{IncompId, Incompatibility, Relation};
|
||||||
use crate::internal::small_map::SmallMap;
|
use crate::internal::small_map::SmallMap;
|
||||||
use crate::package::Package;
|
use crate::package::Package;
|
||||||
use crate::range::Range;
|
|
||||||
use crate::term::Term;
|
use crate::term::Term;
|
||||||
use crate::type_aliases::{Map, SelectedDependencies};
|
use crate::type_aliases::{Map, SelectedDependencies};
|
||||||
use crate::version::Version;
|
use crate::version_set::VersionSet;
|
||||||
|
|
||||||
use super::small_vec::SmallVec;
|
use super::small_vec::SmallVec;
|
||||||
|
|
||||||
|
@ -26,47 +27,100 @@ impl DecisionLevel {
|
||||||
/// The partial solution contains all package assignments,
|
/// The partial solution contains all package assignments,
|
||||||
/// organized by package and historically ordered.
|
/// organized by package and historically ordered.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PartialSolution<P: Package, V: Version> {
|
pub struct PartialSolution<P: Package, VS: VersionSet> {
|
||||||
next_global_index: u32,
|
next_global_index: u32,
|
||||||
current_decision_level: DecisionLevel,
|
current_decision_level: DecisionLevel,
|
||||||
package_assignments: Map<P, PackageAssignments<P, V>>,
|
package_assignments: Map<P, PackageAssignments<P, VS>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Package, VS: VersionSet> Display for PartialSolution<P, VS> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut assignments: Vec<_> = self
|
||||||
|
.package_assignments
|
||||||
|
.iter()
|
||||||
|
.map(|(p, pa)| format!("{}: {}", p, pa))
|
||||||
|
.collect();
|
||||||
|
assignments.sort();
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"next_global_index: {}\ncurrent_decision_level: {:?}\npackage_assignements:\n{}",
|
||||||
|
self.next_global_index,
|
||||||
|
self.current_decision_level,
|
||||||
|
assignments.join("\t\n")
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Package assignments contain the potential decision and derivations
|
/// Package assignments contain the potential decision and derivations
|
||||||
/// that have already been made for a given package,
|
/// that have already been made for a given package,
|
||||||
/// as well as the intersection of terms by all of these.
|
/// as well as the intersection of terms by all of these.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct PackageAssignments<P: Package, V: Version> {
|
struct PackageAssignments<P: Package, VS: VersionSet> {
|
||||||
smallest_decision_level: DecisionLevel,
|
smallest_decision_level: DecisionLevel,
|
||||||
highest_decision_level: DecisionLevel,
|
highest_decision_level: DecisionLevel,
|
||||||
dated_derivations: SmallVec<DatedDerivation<P, V>>,
|
dated_derivations: SmallVec<DatedDerivation<P, VS>>,
|
||||||
assignments_intersection: AssignmentsIntersection<V>,
|
assignments_intersection: AssignmentsIntersection<VS>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Package, VS: VersionSet> Display for PackageAssignments<P, VS> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let derivations: Vec<_> = self
|
||||||
|
.dated_derivations
|
||||||
|
.iter()
|
||||||
|
.map(|dd| dd.to_string())
|
||||||
|
.collect();
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"decision range: {:?}..{:?}\nderivations:\n {}\n,assignments_intersection: {}",
|
||||||
|
self.smallest_decision_level,
|
||||||
|
self.highest_decision_level,
|
||||||
|
derivations.join("\n "),
|
||||||
|
self.assignments_intersection
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct DatedDerivation<P: Package, V: Version> {
|
pub struct DatedDerivation<P: Package, VS: VersionSet> {
|
||||||
global_index: u32,
|
global_index: u32,
|
||||||
decision_level: DecisionLevel,
|
decision_level: DecisionLevel,
|
||||||
cause: IncompId<P, V>,
|
cause: IncompId<P, VS>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Package, VS: VersionSet> Display for DatedDerivation<P, VS> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}, cause: {:?}", self.decision_level, self.cause)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum AssignmentsIntersection<V: Version> {
|
enum AssignmentsIntersection<VS: VersionSet> {
|
||||||
Decision((u32, V, Term<V>)),
|
Decision((u32, VS::V, Term<VS>)),
|
||||||
Derivations(Term<V>),
|
Derivations(Term<VS>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<VS: VersionSet> Display for AssignmentsIntersection<VS> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Decision((lvl, version, _)) => {
|
||||||
|
write!(f, "Decision: level {}, v = {}", lvl, version)
|
||||||
|
}
|
||||||
|
Self::Derivations(term) => write!(f, "Derivations term: {}", term),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum SatisfierSearch<P: Package, V: Version> {
|
pub enum SatisfierSearch<P: Package, VS: VersionSet> {
|
||||||
DifferentDecisionLevels {
|
DifferentDecisionLevels {
|
||||||
previous_satisfier_level: DecisionLevel,
|
previous_satisfier_level: DecisionLevel,
|
||||||
},
|
},
|
||||||
SameDecisionLevels {
|
SameDecisionLevels {
|
||||||
satisfier_cause: IncompId<P, V>,
|
satisfier_cause: IncompId<P, VS>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> PartialSolution<P, V> {
|
impl<P: Package, VS: VersionSet> PartialSolution<P, VS> {
|
||||||
/// Initialize an empty PartialSolution.
|
/// Initialize an empty PartialSolution.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -77,7 +131,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a decision.
|
/// Add a decision.
|
||||||
pub fn add_decision(&mut self, package: P, version: V) {
|
pub fn add_decision(&mut self, package: P, version: VS::V) {
|
||||||
// Check that add_decision is never used in the wrong context.
|
// Check that add_decision is never used in the wrong context.
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
match self.package_assignments.get_mut(&package) {
|
match self.package_assignments.get_mut(&package) {
|
||||||
|
@ -110,8 +164,8 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
pub fn add_derivation(
|
pub fn add_derivation(
|
||||||
&mut self,
|
&mut self,
|
||||||
package: P,
|
package: P,
|
||||||
cause: IncompId<P, V>,
|
cause: IncompId<P, VS>,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) {
|
) {
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
let term = store[cause].get(&package).unwrap().negate();
|
let term = store[cause].get(&package).unwrap().negate();
|
||||||
|
@ -153,7 +207,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
/// selected version (no "decision")
|
/// selected version (no "decision")
|
||||||
/// and if it contains at least one positive derivation term
|
/// and if it contains at least one positive derivation term
|
||||||
/// in the partial solution.
|
/// in the partial solution.
|
||||||
pub fn potential_packages(&self) -> Option<impl Iterator<Item = (&P, &Range<V>)>> {
|
pub fn potential_packages(&self) -> Option<impl Iterator<Item = (&P, &VS)>> {
|
||||||
let mut iter = self
|
let mut iter = self
|
||||||
.package_assignments
|
.package_assignments
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -169,7 +223,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
/// If a partial solution has, for every positive derivation,
|
/// If a partial solution has, for every positive derivation,
|
||||||
/// a corresponding decision that satisfies that assignment,
|
/// a corresponding decision that satisfies that assignment,
|
||||||
/// it's a total solution and version solving has succeeded.
|
/// it's a total solution and version solving has succeeded.
|
||||||
pub fn extract_solution(&self) -> Option<SelectedDependencies<P, V>> {
|
pub fn extract_solution(&self) -> Option<SelectedDependencies<P, VS::V>> {
|
||||||
let mut solution = Map::default();
|
let mut solution = Map::default();
|
||||||
for (p, pa) in &self.package_assignments {
|
for (p, pa) in &self.package_assignments {
|
||||||
match &pa.assignments_intersection {
|
match &pa.assignments_intersection {
|
||||||
|
@ -190,7 +244,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
pub fn backtrack(
|
pub fn backtrack(
|
||||||
&mut self,
|
&mut self,
|
||||||
decision_level: DecisionLevel,
|
decision_level: DecisionLevel,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) {
|
) {
|
||||||
self.current_decision_level = decision_level;
|
self.current_decision_level = decision_level;
|
||||||
self.package_assignments.retain(|p, pa| {
|
self.package_assignments.retain(|p, pa| {
|
||||||
|
@ -223,7 +277,7 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
pa.dated_derivations
|
pa.dated_derivations
|
||||||
.iter()
|
.iter()
|
||||||
.fold(Term::any(), |acc, dated_derivation| {
|
.fold(Term::any(), |acc, dated_derivation| {
|
||||||
let term = store[dated_derivation.cause].get(&p).unwrap().negate();
|
let term = store[dated_derivation.cause].get(p).unwrap().negate();
|
||||||
acc.intersection(&term)
|
acc.intersection(&term)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -240,12 +294,12 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
pub fn add_version(
|
pub fn add_version(
|
||||||
&mut self,
|
&mut self,
|
||||||
package: P,
|
package: P,
|
||||||
version: V,
|
version: VS::V,
|
||||||
new_incompatibilities: std::ops::Range<IncompId<P, V>>,
|
new_incompatibilities: std::ops::Range<IncompId<P, VS>>,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) {
|
) {
|
||||||
let exact = Term::exact(version.clone());
|
let exact = Term::exact(version.clone());
|
||||||
let not_satisfied = |incompat: &Incompatibility<P, V>| {
|
let not_satisfied = |incompat: &Incompatibility<P, VS>| {
|
||||||
incompat.relation(|p| {
|
incompat.relation(|p| {
|
||||||
if p == &package {
|
if p == &package {
|
||||||
Some(&exact)
|
Some(&exact)
|
||||||
|
@ -258,17 +312,24 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
// Check none of the dependencies (new_incompatibilities)
|
// Check none of the dependencies (new_incompatibilities)
|
||||||
// would create a conflict (be satisfied).
|
// would create a conflict (be satisfied).
|
||||||
if store[new_incompatibilities].iter().all(not_satisfied) {
|
if store[new_incompatibilities].iter().all(not_satisfied) {
|
||||||
|
log::info!("add_decision: {} @ {}", package, version);
|
||||||
self.add_decision(package, version);
|
self.add_decision(package, version);
|
||||||
|
} else {
|
||||||
|
log::info!(
|
||||||
|
"not adding {} @ {} because of its dependencies",
|
||||||
|
package,
|
||||||
|
version
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the terms in the partial solution satisfy the incompatibility.
|
/// Check if the terms in the partial solution satisfy the incompatibility.
|
||||||
pub fn relation(&self, incompat: &Incompatibility<P, V>) -> Relation<P> {
|
pub fn relation(&self, incompat: &Incompatibility<P, VS>) -> Relation<P> {
|
||||||
incompat.relation(|package| self.term_intersection_for_package(package))
|
incompat.relation(|package| self.term_intersection_for_package(package))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve intersection of terms related to package.
|
/// Retrieve intersection of terms related to package.
|
||||||
pub fn term_intersection_for_package(&self, package: &P) -> Option<&Term<V>> {
|
pub fn term_intersection_for_package(&self, package: &P) -> Option<&Term<VS>> {
|
||||||
self.package_assignments
|
self.package_assignments
|
||||||
.get(package)
|
.get(package)
|
||||||
.map(|pa| pa.assignments_intersection.term())
|
.map(|pa| pa.assignments_intersection.term())
|
||||||
|
@ -277,9 +338,9 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
/// Figure out if the satisfier and previous satisfier are of different decision levels.
|
/// Figure out if the satisfier and previous satisfier are of different decision levels.
|
||||||
pub fn satisfier_search(
|
pub fn satisfier_search(
|
||||||
&self,
|
&self,
|
||||||
incompat: &Incompatibility<P, V>,
|
incompat: &Incompatibility<P, VS>,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) -> (P, SatisfierSearch<P, V>) {
|
) -> (P, SatisfierSearch<P, VS>) {
|
||||||
let satisfied_map = Self::find_satisfier(incompat, &self.package_assignments, store);
|
let satisfied_map = Self::find_satisfier(incompat, &self.package_assignments, store);
|
||||||
let (satisfier_package, &(satisfier_index, _, satisfier_decision_level)) = satisfied_map
|
let (satisfier_package, &(satisfier_index, _, satisfier_decision_level)) = satisfied_map
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -318,9 +379,9 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
/// It would be nice if we could get rid of it, but I don't know if then it will be possible
|
/// It would be nice if we could get rid of it, but I don't know if then it will be possible
|
||||||
/// to return a coherent previous_satisfier_level.
|
/// to return a coherent previous_satisfier_level.
|
||||||
fn find_satisfier(
|
fn find_satisfier(
|
||||||
incompat: &Incompatibility<P, V>,
|
incompat: &Incompatibility<P, VS>,
|
||||||
package_assignments: &Map<P, PackageAssignments<P, V>>,
|
package_assignments: &Map<P, PackageAssignments<P, VS>>,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) -> SmallMap<P, (usize, u32, DecisionLevel)> {
|
) -> SmallMap<P, (usize, u32, DecisionLevel)> {
|
||||||
let mut satisfied = SmallMap::Empty;
|
let mut satisfied = SmallMap::Empty;
|
||||||
for (package, incompat_term) in incompat.iter() {
|
for (package, incompat_term) in incompat.iter() {
|
||||||
|
@ -337,11 +398,11 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
/// such that incompatibility is satisfied by the partial solution up to
|
/// such that incompatibility is satisfied by the partial solution up to
|
||||||
/// and including that assignment plus satisfier.
|
/// and including that assignment plus satisfier.
|
||||||
fn find_previous_satisfier(
|
fn find_previous_satisfier(
|
||||||
incompat: &Incompatibility<P, V>,
|
incompat: &Incompatibility<P, VS>,
|
||||||
satisfier_package: &P,
|
satisfier_package: &P,
|
||||||
mut satisfied_map: SmallMap<P, (usize, u32, DecisionLevel)>,
|
mut satisfied_map: SmallMap<P, (usize, u32, DecisionLevel)>,
|
||||||
package_assignments: &Map<P, PackageAssignments<P, V>>,
|
package_assignments: &Map<P, PackageAssignments<P, VS>>,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) -> DecisionLevel {
|
) -> DecisionLevel {
|
||||||
// First, let's retrieve the previous derivations and the initial accum_term.
|
// First, let's retrieve the previous derivations and the initial accum_term.
|
||||||
let satisfier_pa = package_assignments.get(satisfier_package).unwrap();
|
let satisfier_pa = package_assignments.get(satisfier_package).unwrap();
|
||||||
|
@ -375,13 +436,13 @@ impl<P: Package, V: Version> PartialSolution<P, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> PackageAssignments<P, V> {
|
impl<P: Package, VS: VersionSet> PackageAssignments<P, VS> {
|
||||||
fn satisfier(
|
fn satisfier(
|
||||||
&self,
|
&self,
|
||||||
package: &P,
|
package: &P,
|
||||||
incompat_term: &Term<V>,
|
incompat_term: &Term<VS>,
|
||||||
start_term: Term<V>,
|
start_term: Term<VS>,
|
||||||
store: &Arena<Incompatibility<P, V>>,
|
store: &Arena<Incompatibility<P, VS>>,
|
||||||
) -> (usize, u32, DecisionLevel) {
|
) -> (usize, u32, DecisionLevel) {
|
||||||
// Term where we accumulate intersections until incompat_term is satisfied.
|
// Term where we accumulate intersections until incompat_term is satisfied.
|
||||||
let mut accum_term = start_term;
|
let mut accum_term = start_term;
|
||||||
|
@ -407,15 +468,24 @@ impl<P: Package, V: Version> PackageAssignments<P, V> {
|
||||||
self.highest_decision_level,
|
self.highest_decision_level,
|
||||||
),
|
),
|
||||||
AssignmentsIntersection::Derivations(_) => {
|
AssignmentsIntersection::Derivations(_) => {
|
||||||
panic!("This must be a decision")
|
unreachable!(
|
||||||
|
concat!(
|
||||||
|
"while processing package {}: ",
|
||||||
|
"accum_term = {} isn't a subset of incompat_term = {}, ",
|
||||||
|
"which means the last assignment should have been a decision, ",
|
||||||
|
"but instead it was a derivation. This shouldn't be possible! ",
|
||||||
|
"(Maybe your Version ordering is broken?)"
|
||||||
|
),
|
||||||
|
package, accum_term, incompat_term
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Version> AssignmentsIntersection<V> {
|
impl<VS: VersionSet> AssignmentsIntersection<VS> {
|
||||||
/// Returns the term intersection of all assignments (decision included).
|
/// Returns the term intersection of all assignments (decision included).
|
||||||
fn term(&self) -> &Term<V> {
|
fn term(&self) -> &Term<VS> {
|
||||||
match self {
|
match self {
|
||||||
Self::Decision((_, _, term)) => term,
|
Self::Decision((_, _, term)) => term,
|
||||||
Self::Derivations(term) => term,
|
Self::Derivations(term) => term,
|
||||||
|
@ -429,7 +499,7 @@ impl<V: Version> AssignmentsIntersection<V> {
|
||||||
fn potential_package_filter<'a, P: Package>(
|
fn potential_package_filter<'a, P: Package>(
|
||||||
&'a self,
|
&'a self,
|
||||||
package: &'a P,
|
package: &'a P,
|
||||||
) -> Option<(&'a P, &'a Range<V>)> {
|
) -> Option<(&'a P, &'a VS)> {
|
||||||
match self {
|
match self {
|
||||||
Self::Decision(_) => None,
|
Self::Decision(_) => None,
|
||||||
Self::Derivations(term_intersection) => {
|
Self::Derivations(term_intersection) => {
|
||||||
|
|
18
vendor/pubgrub/src/internal/small_map.rs
vendored
18
vendor/pubgrub/src/internal/small_map.rs
vendored
|
@ -2,7 +2,7 @@ use crate::type_aliases::Map;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum SmallMap<K, V> {
|
pub enum SmallMap<K, V> {
|
||||||
Empty,
|
Empty,
|
||||||
One([(K, V); 1]),
|
One([(K, V); 1]),
|
||||||
Two([(K, V); 2]),
|
Two([(K, V); 2]),
|
||||||
|
@ -10,7 +10,7 @@ pub(crate) enum SmallMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: PartialEq + Eq + Hash, V> SmallMap<K, V> {
|
impl<K: PartialEq + Eq + Hash, V> SmallMap<K, V> {
|
||||||
pub(crate) fn get(&self, key: &K) -> Option<&V> {
|
pub fn get(&self, key: &K) -> Option<&V> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => None,
|
Self::Empty => None,
|
||||||
Self::One([(k, v)]) if k == key => Some(v),
|
Self::One([(k, v)]) if k == key => Some(v),
|
||||||
|
@ -22,7 +22,7 @@ impl<K: PartialEq + Eq + Hash, V> SmallMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => None,
|
Self::Empty => None,
|
||||||
Self::One([(k, v)]) if k == key => Some(v),
|
Self::One([(k, v)]) if k == key => Some(v),
|
||||||
|
@ -34,7 +34,7 @@ impl<K: PartialEq + Eq + Hash, V> SmallMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove(&mut self, key: &K) -> Option<V> {
|
pub fn remove(&mut self, key: &K) -> Option<V> {
|
||||||
let out;
|
let out;
|
||||||
*self = match std::mem::take(self) {
|
*self = match std::mem::take(self) {
|
||||||
Self::Empty => {
|
Self::Empty => {
|
||||||
|
@ -70,7 +70,7 @@ impl<K: PartialEq + Eq + Hash, V> SmallMap<K, V> {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, key: K, value: V) {
|
pub fn insert(&mut self, key: K, value: V) {
|
||||||
*self = match std::mem::take(self) {
|
*self = match std::mem::take(self) {
|
||||||
Self::Empty => Self::One([(key, value)]),
|
Self::Empty => Self::One([(key, value)]),
|
||||||
Self::One([(k, v)]) => {
|
Self::One([(k, v)]) => {
|
||||||
|
@ -108,7 +108,7 @@ impl<K: Clone + PartialEq + Eq + Hash, V: Clone> SmallMap<K, V> {
|
||||||
/// apply the provided function to both values.
|
/// apply the provided function to both values.
|
||||||
/// If the result is None, remove that key from the merged map,
|
/// If the result is None, remove that key from the merged map,
|
||||||
/// otherwise add the content of the Some(_).
|
/// otherwise add the content of the Some(_).
|
||||||
pub(crate) fn merge<'a>(
|
pub fn merge<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
map_2: impl Iterator<Item = (&'a K, &'a V)>,
|
map_2: impl Iterator<Item = (&'a K, &'a V)>,
|
||||||
f: impl Fn(&V, &V) -> Option<V>,
|
f: impl Fn(&V, &V) -> Option<V>,
|
||||||
|
@ -136,7 +136,7 @@ impl<K, V> Default for SmallMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> SmallMap<K, V> {
|
impl<K, V> SmallMap<K, V> {
|
||||||
pub(crate) fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => 0,
|
Self::Empty => 0,
|
||||||
Self::One(_) => 1,
|
Self::One(_) => 1,
|
||||||
|
@ -147,7 +147,7 @@ impl<K, V> SmallMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Eq + Hash + Clone, V: Clone> SmallMap<K, V> {
|
impl<K: Eq + Hash + Clone, V: Clone> SmallMap<K, V> {
|
||||||
pub(crate) fn as_map(&self) -> Map<K, V> {
|
pub fn as_map(&self) -> Map<K, V> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => Map::default(),
|
Self::Empty => Map::default(),
|
||||||
Self::One([(k, v)]) => {
|
Self::One([(k, v)]) => {
|
||||||
|
@ -184,7 +184,7 @@ impl<'a, K: 'a, V: 'a> Iterator for IterSmallMap<'a, K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> SmallMap<K, V> {
|
impl<K, V> SmallMap<K, V> {
|
||||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => IterSmallMap::Inline([].iter()),
|
Self::Empty => IterSmallMap::Inline([].iter()),
|
||||||
Self::One(data) => IterSmallMap::Inline(data.iter()),
|
Self::One(data) => IterSmallMap::Inline(data.iter()),
|
||||||
|
|
83
vendor/pubgrub/src/internal/small_vec.rs
vendored
83
vendor/pubgrub/src/internal/small_vec.rs
vendored
|
@ -1,4 +1,5 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -108,6 +109,13 @@ impl<T: fmt::Debug> fmt::Debug for SmallVec<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Hash> Hash for SmallVec<T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.len().hash(state);
|
||||||
|
Hash::hash_slice(self.as_slice(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
impl<T: serde::Serialize> serde::Serialize for SmallVec<T> {
|
impl<T: serde::Serialize> serde::Serialize for SmallVec<T> {
|
||||||
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
|
@ -118,13 +126,70 @@ impl<T: serde::Serialize> serde::Serialize for SmallVec<T> {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for SmallVec<T> {
|
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for SmallVec<T> {
|
||||||
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
|
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
|
||||||
let items: Vec<T> = serde::Deserialize::deserialize(d)?;
|
struct SmallVecVisitor<T> {
|
||||||
|
marker: std::marker::PhantomData<T>,
|
||||||
let mut v = Self::empty();
|
}
|
||||||
for item in items {
|
|
||||||
v.push(item);
|
impl<'de, T> serde::de::Visitor<'de> for SmallVecVisitor<T>
|
||||||
|
where
|
||||||
|
T: serde::Deserialize<'de>,
|
||||||
|
{
|
||||||
|
type Value = SmallVec<T>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a sequence")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut values = SmallVec::empty();
|
||||||
|
while let Some(value) = seq.next_element()? {
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
Ok(values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let visitor = SmallVecVisitor {
|
||||||
|
marker: Default::default(),
|
||||||
|
};
|
||||||
|
d.deserialize_seq(visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoIterator for SmallVec<T> {
|
||||||
|
type Item = T;
|
||||||
|
type IntoIter = SmallVecIntoIter<T>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
match self {
|
||||||
|
SmallVec::Empty => SmallVecIntoIter::Empty,
|
||||||
|
SmallVec::One(a) => SmallVecIntoIter::One(IntoIterator::into_iter(a)),
|
||||||
|
SmallVec::Two(a) => SmallVecIntoIter::Two(IntoIterator::into_iter(a)),
|
||||||
|
SmallVec::Flexible(v) => SmallVecIntoIter::Flexible(IntoIterator::into_iter(v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SmallVecIntoIter<T> {
|
||||||
|
Empty,
|
||||||
|
One(<[T; 1] as IntoIterator>::IntoIter),
|
||||||
|
Two(<[T; 2] as IntoIterator>::IntoIter),
|
||||||
|
Flexible(<Vec<T> as IntoIterator>::IntoIter),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for SmallVecIntoIter<T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self {
|
||||||
|
SmallVecIntoIter::Empty => None,
|
||||||
|
SmallVecIntoIter::One(it) => it.next(),
|
||||||
|
SmallVecIntoIter::Two(it) => it.next(),
|
||||||
|
SmallVecIntoIter::Flexible(it) => it.next(),
|
||||||
}
|
}
|
||||||
Ok(v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,11 +202,11 @@ pub mod tests {
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn push_and_pop(comands: Vec<Option<u8>>) {
|
fn push_and_pop(commands: Vec<Option<u8>>) {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
let mut sv = SmallVec::Empty;
|
let mut sv = SmallVec::Empty;
|
||||||
for comand in comands {
|
for command in commands {
|
||||||
match comand {
|
match command {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
v.push(i);
|
v.push(i);
|
||||||
sv.push(i);
|
sv.push(i);
|
||||||
|
|
36
vendor/pubgrub/src/lib.rs
vendored
36
vendor/pubgrub/src/lib.rs
vendored
|
@ -1,5 +1,7 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
#![allow(clippy::all, unreachable_pub)]
|
||||||
|
|
||||||
//! PubGrub version solving algorithm.
|
//! PubGrub version solving algorithm.
|
||||||
//!
|
//!
|
||||||
//! Version solving consists in efficiently finding a set of packages and versions
|
//! Version solving consists in efficiently finding a set of packages and versions
|
||||||
|
@ -49,15 +51,17 @@
|
||||||
//! # use pubgrub::solver::{OfflineDependencyProvider, resolve};
|
//! # use pubgrub::solver::{OfflineDependencyProvider, resolve};
|
||||||
//! # use pubgrub::version::NumberVersion;
|
//! # use pubgrub::version::NumberVersion;
|
||||||
//! # use pubgrub::range::Range;
|
//! # use pubgrub::range::Range;
|
||||||
//! #
|
//!
|
||||||
//! let mut dependency_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
|
//! type NumVS = Range<NumberVersion>;
|
||||||
|
//!
|
||||||
|
//! let mut dependency_provider = OfflineDependencyProvider::<&str, NumVS>::new();
|
||||||
//!
|
//!
|
||||||
//! dependency_provider.add_dependencies(
|
//! dependency_provider.add_dependencies(
|
||||||
//! "root", 1, vec![("menu", Range::any()), ("icons", Range::any())],
|
//! "root", 1, [("menu", Range::full()), ("icons", Range::full())],
|
||||||
//! );
|
//! );
|
||||||
//! dependency_provider.add_dependencies("menu", 1, vec![("dropdown", Range::any())]);
|
//! dependency_provider.add_dependencies("menu", 1, [("dropdown", Range::full())]);
|
||||||
//! dependency_provider.add_dependencies("dropdown", 1, vec![("icons", Range::any())]);
|
//! dependency_provider.add_dependencies("dropdown", 1, [("icons", Range::full())]);
|
||||||
//! dependency_provider.add_dependencies("icons", 1, vec![]);
|
//! dependency_provider.add_dependencies("icons", 1, []);
|
||||||
//!
|
//!
|
||||||
//! // Run the algorithm.
|
//! // Run the algorithm.
|
||||||
//! let solution = resolve(&dependency_provider, "root", 1).unwrap();
|
//! let solution = resolve(&dependency_provider, "root", 1).unwrap();
|
||||||
|
@ -84,8 +88,10 @@
|
||||||
//! #
|
//! #
|
||||||
//! # struct MyDependencyProvider;
|
//! # struct MyDependencyProvider;
|
||||||
//! #
|
//! #
|
||||||
//! impl DependencyProvider<String, SemanticVersion> for MyDependencyProvider {
|
//! type SemVS = Range<SemanticVersion>;
|
||||||
//! fn choose_package_version<T: Borrow<String>, U: Borrow<Range<SemanticVersion>>>(&self,packages: impl Iterator<Item=(T, U)>) -> Result<(T, Option<SemanticVersion>), Box<dyn Error>> {
|
//!
|
||||||
|
//! impl DependencyProvider<String, SemVS> for MyDependencyProvider {
|
||||||
|
//! fn choose_package_version<T: Borrow<String>, U: Borrow<SemVS>>(&self,packages: impl Iterator<Item=(T, U)>) -> Result<(T, Option<SemanticVersion>), Box<dyn Error + Send + Sync>> {
|
||||||
//! unimplemented!()
|
//! unimplemented!()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
@ -93,7 +99,7 @@
|
||||||
//! &self,
|
//! &self,
|
||||||
//! package: &String,
|
//! package: &String,
|
||||||
//! version: &SemanticVersion,
|
//! version: &SemanticVersion,
|
||||||
//! ) -> Result<Dependencies<String, SemanticVersion>, Box<dyn Error>> {
|
//! ) -> Result<Dependencies<String, SemVS>, Box<dyn Error + Send + Sync>> {
|
||||||
//! unimplemented!()
|
//! unimplemented!()
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
@ -153,13 +159,13 @@
|
||||||
//! [Output](crate::report::Reporter::Output) type and a single method.
|
//! [Output](crate::report::Reporter::Output) type and a single method.
|
||||||
//! ```
|
//! ```
|
||||||
//! # use pubgrub::package::Package;
|
//! # use pubgrub::package::Package;
|
||||||
//! # use pubgrub::version::Version;
|
//! # use pubgrub::version_set::VersionSet;
|
||||||
//! # use pubgrub::report::DerivationTree;
|
//! # use pubgrub::report::DerivationTree;
|
||||||
//! #
|
//! #
|
||||||
//! pub trait Reporter<P: Package, V: Version> {
|
//! pub trait Reporter<P: Package, VS: VersionSet> {
|
||||||
//! type Output;
|
//! type Output;
|
||||||
//!
|
//!
|
||||||
//! fn report(derivation_tree: &DerivationTree<P, V>) -> Self::Output;
|
//! fn report(derivation_tree: &DerivationTree<P, VS>) -> Self::Output;
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//! Implementing a [Reporter](crate::report::Reporter) may involve a lot of heuristics
|
//! Implementing a [Reporter](crate::report::Reporter) may involve a lot of heuristics
|
||||||
|
@ -173,8 +179,11 @@
|
||||||
//! # use pubgrub::report::{DefaultStringReporter, Reporter};
|
//! # use pubgrub::report::{DefaultStringReporter, Reporter};
|
||||||
//! # use pubgrub::error::PubGrubError;
|
//! # use pubgrub::error::PubGrubError;
|
||||||
//! # use pubgrub::version::NumberVersion;
|
//! # use pubgrub::version::NumberVersion;
|
||||||
|
//! # use pubgrub::range::Range;
|
||||||
//! #
|
//! #
|
||||||
//! # let dependency_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
|
//! # type NumVS = Range<NumberVersion>;
|
||||||
|
//! #
|
||||||
|
//! # let dependency_provider = OfflineDependencyProvider::<&str, NumVS>::new();
|
||||||
//! # let root_package = "root";
|
//! # let root_package = "root";
|
||||||
//! # let root_version = 1;
|
//! # let root_version = 1;
|
||||||
//! #
|
//! #
|
||||||
|
@ -217,5 +226,6 @@ pub mod solver;
|
||||||
pub mod term;
|
pub mod term;
|
||||||
pub mod type_aliases;
|
pub mod type_aliases;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
pub mod version_set;
|
||||||
|
|
||||||
mod internal;
|
mod internal;
|
||||||
|
|
6
vendor/pubgrub/src/package.rs
vendored
6
vendor/pubgrub/src/package.rs
vendored
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
//! Trait for identifying packages.
|
//! Trait for identifying packages.
|
||||||
//! Automatically implemented for traits implementing
|
//! Automatically implemented for traits implementing
|
||||||
//! [Clone] + [Eq] + [Hash] + [Debug] + [Display](std::fmt::Display).
|
//! [Clone] + [Eq] + [Hash] + [Debug] + [Display].
|
||||||
|
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// Trait for identifying packages.
|
/// Trait for identifying packages.
|
||||||
/// Automatically implemented for types already implementing
|
/// Automatically implemented for types already implementing
|
||||||
/// [Clone] + [Eq] + [Hash] + [Debug] + [Display](std::fmt::Display).
|
/// [Clone] + [Eq] + [Hash] + [Debug] + [Display].
|
||||||
pub trait Package: Clone + Eq + Hash + Debug + Display {}
|
pub trait Package: Clone + Eq + Hash + Debug + Display {}
|
||||||
|
|
||||||
/// Automatically implement the Package trait for any type
|
/// Automatically implement the Package trait for any type
|
||||||
/// that already implement [Clone] + [Eq] + [Hash] + [Debug] + [Display](std::fmt::Display).
|
/// that already implement [Clone] + [Eq] + [Hash] + [Debug] + [Display].
|
||||||
impl<T: Clone + Eq + Hash + Debug + Display> Package for T {}
|
impl<T: Clone + Eq + Hash + Debug + Display> Package for T {}
|
||||||
|
|
686
vendor/pubgrub/src/range.rs
vendored
686
vendor/pubgrub/src/range.rs
vendored
|
@ -7,287 +7,425 @@
|
||||||
//! of the ranges building blocks.
|
//! of the ranges building blocks.
|
||||||
//!
|
//!
|
||||||
//! Those building blocks are:
|
//! Those building blocks are:
|
||||||
//! - [none()](Range::none): the empty set
|
//! - [empty()](Range::empty): the empty set
|
||||||
//! - [any()](Range::any): the set of all possible versions
|
//! - [full()](Range::full): the set of all possible versions
|
||||||
//! - [exact(v)](Range::exact): the set containing only the version v
|
//! - [singleton(v)](Range::singleton): the set containing only the version v
|
||||||
//! - [higher_than(v)](Range::higher_than): the set defined by `v <= versions`
|
//! - [higher_than(v)](Range::higher_than): the set defined by `v <= versions`
|
||||||
|
//! - [strictly_higher_than(v)](Range::strictly_higher_than): the set defined by `v < versions`
|
||||||
|
//! - [lower_than(v)](Range::lower_than): the set defined by `versions <= v`
|
||||||
//! - [strictly_lower_than(v)](Range::strictly_lower_than): the set defined by `versions < v`
|
//! - [strictly_lower_than(v)](Range::strictly_lower_than): the set defined by `versions < v`
|
||||||
//! - [between(v1, v2)](Range::between): the set defined by `v1 <= versions < v2`
|
//! - [between(v1, v2)](Range::between): the set defined by `v1 <= versions < v2`
|
||||||
|
//!
|
||||||
|
//! Ranges can be created from any type that implements [`Ord`] + [`Clone`].
|
||||||
|
//!
|
||||||
|
//! In order to advance the solver front, comparisons of versions sets are necessary in the algorithm.
|
||||||
|
//! To do those comparisons between two sets S1 and S2 we use the mathematical property that S1 ⊂ S2 if and only if S1 ∩ S2 == S1.
|
||||||
|
//! We can thus compute an intersection and evaluate an equality to answer if S1 is a subset of S2.
|
||||||
|
//! But this means that the implementation of equality must be correct semantically.
|
||||||
|
//! In practice, if equality is derived automatically, this means sets must have unique representations.
|
||||||
|
//!
|
||||||
|
//! By migrating from a custom representation for discrete sets in v0.2
|
||||||
|
//! to a generic bounded representation for continuous sets in v0.3
|
||||||
|
//! we are potentially breaking that assumption in two ways:
|
||||||
|
//!
|
||||||
|
//! 1. Minimal and maximal `Unbounded` values can be replaced by their equivalent if it exists.
|
||||||
|
//! 2. Simplifying adjacent bounds of discrete sets cannot be detected and automated in the generic intersection code.
|
||||||
|
//!
|
||||||
|
//! An example for each can be given when `T` is `u32`.
|
||||||
|
//! First, we can have both segments `S1 = (Unbounded, Included(42u32))` and `S2 = (Included(0), Included(42u32))`
|
||||||
|
//! that represent the same segment but are structurally different.
|
||||||
|
//! Thus, a derived equality check would answer `false` to `S1 == S2` while it's true.
|
||||||
|
//!
|
||||||
|
//! Second both segments `S1 = (Included(1), Included(5))` and `S2 = (Included(1), Included(3)) + (Included(4), Included(5))` are equal.
|
||||||
|
//! But without asking the user to provide a `bump` function for discrete sets,
|
||||||
|
//! the algorithm is not able tell that the space between the right `Included(3)` bound and the left `Included(4)` bound is empty.
|
||||||
|
//! Thus the algorithm is not able to reduce S2 to its canonical S1 form while computing sets operations like intersections in the generic code.
|
||||||
|
//!
|
||||||
|
//! This is likely to lead to user facing theoretically correct but practically nonsensical ranges,
|
||||||
|
//! like (Unbounded, Excluded(0)) or (Excluded(6), Excluded(7)).
|
||||||
|
//! In general nonsensical inputs often lead to hard to track bugs.
|
||||||
|
//! But as far as we can tell this should work in practice.
|
||||||
|
//! So for now this crate only provides an implementation for continuous ranges.
|
||||||
|
//! With the v0.3 api the user could choose to bring back the discrete implementation from v0.2, as documented in the guide.
|
||||||
|
//! If doing so regularly fixes bugs seen by users, we will bring it back into the core library.
|
||||||
|
//! If we do not see practical bugs, or we get a formal proof that the code cannot lead to error states, then we may remove this warning.
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use crate::{internal::small_vec::SmallVec, version_set::VersionSet};
|
||||||
use std::fmt;
|
use std::ops::RangeBounds;
|
||||||
|
use std::{
|
||||||
|
fmt::{Debug, Display, Formatter},
|
||||||
|
ops::Bound::{self, Excluded, Included, Unbounded},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::internal::small_vec::SmallVec;
|
/// A Range represents multiple intervals of a continuous range of monotone increasing
|
||||||
use crate::version::Version;
|
/// values.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
/// A Range is a set of versions.
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
pub struct Range<V: Version> {
|
pub struct Range<V> {
|
||||||
segments: SmallVec<Interval<V>>,
|
segments: SmallVec<Interval<V>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Interval<V> = (V, Option<V>);
|
type Interval<V> = (Bound<V>, Bound<V>);
|
||||||
|
|
||||||
// Range building blocks.
|
impl<V> Range<V> {
|
||||||
impl<V: Version> Range<V> {
|
|
||||||
/// Empty set of versions.
|
/// Empty set of versions.
|
||||||
pub fn none() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
segments: SmallVec::empty(),
|
segments: SmallVec::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of all possible versions.
|
/// Set of all possible versions
|
||||||
pub fn any() -> Self {
|
pub fn full() -> Self {
|
||||||
Self::higher_than(V::lowest())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set containing exactly one version.
|
|
||||||
pub fn exact(v: impl Into<V>) -> Self {
|
|
||||||
let v = v.into();
|
|
||||||
Self {
|
Self {
|
||||||
segments: SmallVec::one((v.clone(), Some(v.bump()))),
|
segments: SmallVec::one((Unbounded, Unbounded)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of all versions higher or equal to some version.
|
/// Set of all versions higher or equal to some version
|
||||||
pub fn higher_than(v: impl Into<V>) -> Self {
|
pub fn higher_than(v: impl Into<V>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
segments: SmallVec::one((v.into(), None)),
|
segments: SmallVec::one((Included(v.into()), Unbounded)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of all versions strictly lower than some version.
|
/// Set of all versions higher to some version
|
||||||
|
pub fn strictly_higher_than(v: impl Into<V>) -> Self {
|
||||||
|
Self {
|
||||||
|
segments: SmallVec::one((Excluded(v.into()), Unbounded)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set of all versions lower to some version
|
||||||
pub fn strictly_lower_than(v: impl Into<V>) -> Self {
|
pub fn strictly_lower_than(v: impl Into<V>) -> Self {
|
||||||
let v = v.into();
|
Self {
|
||||||
if v == V::lowest() {
|
segments: SmallVec::one((Unbounded, Excluded(v.into()))),
|
||||||
Self::none()
|
|
||||||
} else {
|
|
||||||
Self {
|
|
||||||
segments: SmallVec::one((V::lowest(), Some(v))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set of all versions comprised between two given versions.
|
/// Set of all versions lower or equal to some version
|
||||||
/// The lower bound is included and the higher bound excluded.
|
pub fn lower_than(v: impl Into<V>) -> Self {
|
||||||
/// `v1 <= v < v2`.
|
Self {
|
||||||
|
segments: SmallVec::one((Unbounded, Included(v.into()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set of versions greater or equal to `v1` but less than `v2`.
|
||||||
pub fn between(v1: impl Into<V>, v2: impl Into<V>) -> Self {
|
pub fn between(v1: impl Into<V>, v2: impl Into<V>) -> Self {
|
||||||
let v1 = v1.into();
|
Self {
|
||||||
let v2 = v2.into();
|
segments: SmallVec::one((Included(v1.into()), Excluded(v2.into()))),
|
||||||
if v1 < v2 {
|
|
||||||
Self {
|
|
||||||
segments: SmallVec::one((v1, Some(v2))),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Self::none()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set operations.
|
impl<V: Clone> Range<V> {
|
||||||
impl<V: Version> Range<V> {
|
/// Set containing exactly one version
|
||||||
// Negate ##################################################################
|
pub fn singleton(v: impl Into<V>) -> Self {
|
||||||
|
let v = v.into();
|
||||||
|
Self {
|
||||||
|
segments: SmallVec::one((Included(v.clone()), Included(v))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute the complement set of versions.
|
/// Returns the complement of this Range.
|
||||||
pub fn negate(&self) -> Self {
|
pub fn complement(&self) -> Self {
|
||||||
match self.segments.first() {
|
match self.segments.first() {
|
||||||
None => Self::any(), // Complement of ∅ is *
|
// Complement of ∅ is ∞
|
||||||
|
None => Self::full(),
|
||||||
|
|
||||||
|
// Complement of ∞ is ∅
|
||||||
|
Some((Unbounded, Unbounded)) => Self::empty(),
|
||||||
|
|
||||||
// First high bound is +∞
|
// First high bound is +∞
|
||||||
Some((v, None)) => {
|
Some((Included(v), Unbounded)) => Self::strictly_lower_than(v.clone()),
|
||||||
// Complement of * is ∅
|
Some((Excluded(v), Unbounded)) => Self::lower_than(v.clone()),
|
||||||
if v == &V::lowest() {
|
|
||||||
Self::none()
|
|
||||||
// Complement of "v <= _" is "_ < v"
|
|
||||||
} else {
|
|
||||||
Self::strictly_lower_than(v.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// First high bound is not +∞
|
Some((Unbounded, Included(v))) => {
|
||||||
Some((v1, Some(v2))) => {
|
Self::negate_segments(Excluded(v.clone()), &self.segments[1..])
|
||||||
if v1 == &V::lowest() {
|
|
||||||
Self::negate_segments(v2.clone(), &self.segments[1..])
|
|
||||||
} else {
|
|
||||||
Self::negate_segments(V::lowest(), &self.segments)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Some((Unbounded, Excluded(v))) => {
|
||||||
|
Self::negate_segments(Included(v.clone()), &self.segments[1..])
|
||||||
|
}
|
||||||
|
Some((Included(_), Included(_)))
|
||||||
|
| Some((Included(_), Excluded(_)))
|
||||||
|
| Some((Excluded(_), Included(_)))
|
||||||
|
| Some((Excluded(_), Excluded(_))) => Self::negate_segments(Unbounded, &self.segments),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function performing the negation of intervals in segments.
|
/// Helper function performing the negation of intervals in segments.
|
||||||
/// For example:
|
fn negate_segments(start: Bound<V>, segments: &[Interval<V>]) -> Self {
|
||||||
/// [ (v1, None) ] => [ (start, Some(v1)) ]
|
let mut complement_segments: SmallVec<Interval<V>> = SmallVec::empty();
|
||||||
/// [ (v1, Some(v2)) ] => [ (start, Some(v1)), (v2, None) ]
|
let mut start = start;
|
||||||
fn negate_segments(start: V, segments: &[Interval<V>]) -> Range<V> {
|
for (v1, v2) in segments {
|
||||||
let mut complement_segments = SmallVec::empty();
|
complement_segments.push((
|
||||||
let mut start = Some(start);
|
start,
|
||||||
for (v1, maybe_v2) in segments {
|
match v1 {
|
||||||
// start.unwrap() is fine because `segments` is not exposed,
|
Included(v) => Excluded(v.clone()),
|
||||||
// and our usage guaranties that only the last segment may contain a None.
|
Excluded(v) => Included(v.clone()),
|
||||||
complement_segments.push((start.unwrap(), Some(v1.to_owned())));
|
Unbounded => unreachable!(),
|
||||||
start = maybe_v2.to_owned();
|
},
|
||||||
|
));
|
||||||
|
start = match v2 {
|
||||||
|
Included(v) => Excluded(v.clone()),
|
||||||
|
Excluded(v) => Included(v.clone()),
|
||||||
|
Unbounded => Unbounded,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(last) = start {
|
if !matches!(start, Unbounded) {
|
||||||
complement_segments.push((last, None));
|
complement_segments.push((start, Unbounded));
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
segments: complement_segments,
|
segments: complement_segments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union and intersection ##################################################
|
|
||||||
|
|
||||||
/// Compute the union of two sets of versions.
|
|
||||||
pub fn union(&self, other: &Self) -> Self {
|
|
||||||
self.negate().intersection(&other.negate()).negate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute the intersection of two sets of versions.
|
|
||||||
pub fn intersection(&self, other: &Self) -> Self {
|
|
||||||
let mut segments = SmallVec::empty();
|
|
||||||
let mut left_iter = self.segments.iter();
|
|
||||||
let mut right_iter = other.segments.iter();
|
|
||||||
let mut left = left_iter.next();
|
|
||||||
let mut right = right_iter.next();
|
|
||||||
loop {
|
|
||||||
match (left, right) {
|
|
||||||
// Both left and right still contain a finite interval:
|
|
||||||
(Some((l1, Some(l2))), Some((r1, Some(r2)))) => {
|
|
||||||
if l2 <= r1 {
|
|
||||||
// Intervals are disjoint, progress on the left.
|
|
||||||
left = left_iter.next();
|
|
||||||
} else if r2 <= l1 {
|
|
||||||
// Intervals are disjoint, progress on the right.
|
|
||||||
right = right_iter.next();
|
|
||||||
} else {
|
|
||||||
// Intervals are not disjoint.
|
|
||||||
let start = l1.max(r1).to_owned();
|
|
||||||
if l2 < r2 {
|
|
||||||
segments.push((start, Some(l2.to_owned())));
|
|
||||||
left = left_iter.next();
|
|
||||||
} else {
|
|
||||||
segments.push((start, Some(r2.to_owned())));
|
|
||||||
right = right_iter.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right contains an infinite interval:
|
|
||||||
(Some((l1, Some(l2))), Some((r1, None))) => match l2.cmp(r1) {
|
|
||||||
Ordering::Less => {
|
|
||||||
left = left_iter.next();
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
for l in left_iter.cloned() {
|
|
||||||
segments.push(l)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
let start = l1.max(r1).to_owned();
|
|
||||||
segments.push((start, Some(l2.to_owned())));
|
|
||||||
for l in left_iter.cloned() {
|
|
||||||
segments.push(l)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Left contains an infinite interval:
|
|
||||||
(Some((l1, None)), Some((r1, Some(r2)))) => match r2.cmp(l1) {
|
|
||||||
Ordering::Less => {
|
|
||||||
right = right_iter.next();
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
for r in right_iter.cloned() {
|
|
||||||
segments.push(r)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
let start = l1.max(r1).to_owned();
|
|
||||||
segments.push((start, Some(r2.to_owned())));
|
|
||||||
for r in right_iter.cloned() {
|
|
||||||
segments.push(r)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Both sides contain an infinite interval:
|
|
||||||
(Some((l1, None)), Some((r1, None))) => {
|
|
||||||
let start = l1.max(r1).to_owned();
|
|
||||||
segments.push((start, None));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left or right has ended.
|
|
||||||
_ => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self { segments }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other useful functions.
|
impl<V: Ord> Range<V> {
|
||||||
impl<V: Version> Range<V> {
|
/// Convert to something that can be used with
|
||||||
/// Check if a range contains a given version.
|
/// [BTreeMap::range](std::collections::BTreeMap::range).
|
||||||
pub fn contains(&self, version: &V) -> bool {
|
/// All versions contained in self, will be in the output,
|
||||||
for (v1, maybe_v2) in &self.segments {
|
/// but there may be versions in the output that are not contained in self.
|
||||||
match maybe_v2 {
|
/// Returns None if the range is empty.
|
||||||
None => return v1 <= version,
|
pub fn bounding_range(&self) -> Option<(Bound<&V>, Bound<&V>)> {
|
||||||
Some(v2) => {
|
self.segments.first().map(|(start, _)| {
|
||||||
if version < v1 {
|
let end = self
|
||||||
return false;
|
.segments
|
||||||
} else if version < v2 {
|
.last()
|
||||||
return true;
|
.expect("if there is a first element, there must be a last element");
|
||||||
}
|
(bound_as_ref(start), bound_as_ref(&end.1))
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the this Range contains the specified value.
|
||||||
|
pub fn contains(&self, v: &V) -> bool {
|
||||||
|
for segment in self.segments.iter() {
|
||||||
|
if match segment {
|
||||||
|
(Unbounded, Unbounded) => true,
|
||||||
|
(Unbounded, Included(end)) => v <= end,
|
||||||
|
(Unbounded, Excluded(end)) => v < end,
|
||||||
|
(Included(start), Unbounded) => v >= start,
|
||||||
|
(Included(start), Included(end)) => v >= start && v <= end,
|
||||||
|
(Included(start), Excluded(end)) => v >= start && v < end,
|
||||||
|
(Excluded(start), Unbounded) => v > start,
|
||||||
|
(Excluded(start), Included(end)) => v > start && v <= end,
|
||||||
|
(Excluded(start), Excluded(end)) => v > start && v < end,
|
||||||
|
} {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the lowest version in the range (if there is one).
|
/// Construct a simple range from anything that impls [RangeBounds] like `v1..v2`.
|
||||||
pub fn lowest_version(&self) -> Option<V> {
|
pub fn from_range_bounds<R, IV>(bounds: R) -> Self
|
||||||
self.segments.first().map(|(start, _)| start).cloned()
|
where
|
||||||
|
R: RangeBounds<IV>,
|
||||||
|
IV: Clone + Into<V>,
|
||||||
|
{
|
||||||
|
let start = match bounds.start_bound() {
|
||||||
|
Included(v) => Included(v.clone().into()),
|
||||||
|
Excluded(v) => Excluded(v.clone().into()),
|
||||||
|
Unbounded => Unbounded,
|
||||||
|
};
|
||||||
|
let end = match bounds.end_bound() {
|
||||||
|
Included(v) => Included(v.clone().into()),
|
||||||
|
Excluded(v) => Excluded(v.clone().into()),
|
||||||
|
Unbounded => Unbounded,
|
||||||
|
};
|
||||||
|
if valid_segment(&start, &end) {
|
||||||
|
Self {
|
||||||
|
segments: SmallVec::one((start, end)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_invariants(self) -> Self {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
for p in self.segments.as_slice().windows(2) {
|
||||||
|
match (&p[0].1, &p[1].0) {
|
||||||
|
(Included(l_end), Included(r_start)) => assert!(l_end < r_start),
|
||||||
|
(Included(l_end), Excluded(r_start)) => assert!(l_end < r_start),
|
||||||
|
(Excluded(l_end), Included(r_start)) => assert!(l_end < r_start),
|
||||||
|
(Excluded(l_end), Excluded(r_start)) => assert!(l_end <= r_start),
|
||||||
|
(_, Unbounded) => panic!(),
|
||||||
|
(Unbounded, _) => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (s, e) in self.segments.iter() {
|
||||||
|
assert!(valid_segment(s, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of [`Bound::as_ref`] which is currently marked as unstable.
|
||||||
|
fn bound_as_ref<V>(bound: &Bound<V>) -> Bound<&V> {
|
||||||
|
match bound {
|
||||||
|
Included(v) => Included(v),
|
||||||
|
Excluded(v) => Excluded(v),
|
||||||
|
Unbounded => Unbounded,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_segment<T: PartialOrd>(start: &Bound<T>, end: &Bound<T>) -> bool {
|
||||||
|
match (start, end) {
|
||||||
|
(Included(s), Included(e)) => s <= e,
|
||||||
|
(Included(s), Excluded(e)) => s < e,
|
||||||
|
(Excluded(s), Included(e)) => s < e,
|
||||||
|
(Excluded(s), Excluded(e)) => s < e,
|
||||||
|
(Unbounded, _) | (_, Unbounded) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: Ord + Clone> Range<V> {
|
||||||
|
/// Computes the union of this `Range` and another.
|
||||||
|
pub fn union(&self, other: &Self) -> Self {
|
||||||
|
self.complement()
|
||||||
|
.intersection(&other.complement())
|
||||||
|
.complement()
|
||||||
|
.check_invariants()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the intersection of two sets of versions.
|
||||||
|
pub fn intersection(&self, other: &Self) -> Self {
|
||||||
|
let mut segments: SmallVec<Interval<V>> = SmallVec::empty();
|
||||||
|
let mut left_iter = self.segments.iter().peekable();
|
||||||
|
let mut right_iter = other.segments.iter().peekable();
|
||||||
|
|
||||||
|
while let (Some((left_start, left_end)), Some((right_start, right_end))) =
|
||||||
|
(left_iter.peek(), right_iter.peek())
|
||||||
|
{
|
||||||
|
let start = match (left_start, right_start) {
|
||||||
|
(Included(l), Included(r)) => Included(std::cmp::max(l, r)),
|
||||||
|
(Excluded(l), Excluded(r)) => Excluded(std::cmp::max(l, r)),
|
||||||
|
|
||||||
|
(Included(i), Excluded(e)) | (Excluded(e), Included(i)) if i <= e => Excluded(e),
|
||||||
|
(Included(i), Excluded(e)) | (Excluded(e), Included(i)) if e < i => Included(i),
|
||||||
|
(s, Unbounded) | (Unbounded, s) => bound_as_ref(s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.cloned();
|
||||||
|
let end = match (left_end, right_end) {
|
||||||
|
(Included(l), Included(r)) => Included(std::cmp::min(l, r)),
|
||||||
|
(Excluded(l), Excluded(r)) => Excluded(std::cmp::min(l, r)),
|
||||||
|
|
||||||
|
(Included(i), Excluded(e)) | (Excluded(e), Included(i)) if i >= e => Excluded(e),
|
||||||
|
(Included(i), Excluded(e)) | (Excluded(e), Included(i)) if e > i => Included(i),
|
||||||
|
(s, Unbounded) | (Unbounded, s) => bound_as_ref(s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
.cloned();
|
||||||
|
left_iter.next_if(|(_, e)| e == &end);
|
||||||
|
right_iter.next_if(|(_, e)| e == &end);
|
||||||
|
if valid_segment(&start, &end) {
|
||||||
|
segments.push((start, end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { segments }.check_invariants()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug + Display + Clone + Eq + Ord> VersionSet for Range<T> {
|
||||||
|
type V = T;
|
||||||
|
|
||||||
|
fn empty() -> Self {
|
||||||
|
Range::empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn singleton(v: Self::V) -> Self {
|
||||||
|
Range::singleton(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complement(&self) -> Self {
|
||||||
|
Range::complement(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersection(&self, other: &Self) -> Self {
|
||||||
|
Range::intersection(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, v: &Self::V) -> bool {
|
||||||
|
Range::contains(self, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn full() -> Self {
|
||||||
|
Range::full()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn union(&self, other: &Self) -> Self {
|
||||||
|
Range::union(self, other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// REPORT ######################################################################
|
// REPORT ######################################################################
|
||||||
|
|
||||||
impl<V: Version> fmt::Display for Range<V> {
|
impl<V: Display + Eq> Display for Range<V> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.segments.as_slice() {
|
if self.segments.is_empty() {
|
||||||
[] => write!(f, "∅"),
|
write!(f, "∅")?;
|
||||||
[(start, None)] if start == &V::lowest() => write!(f, "∗"),
|
} else {
|
||||||
[(start, None)] => write!(f, "{} <= v", start),
|
for (idx, segment) in self.segments.iter().enumerate() {
|
||||||
[(start, Some(end))] if end == &start.bump() => write!(f, "{}", start),
|
if idx > 0 {
|
||||||
[(start, Some(end))] if start == &V::lowest() => write!(f, "v < {}", end),
|
write!(f, ", ")?;
|
||||||
[(start, Some(end))] => write!(f, "{} <= v < {}", start, end),
|
}
|
||||||
more_than_one_interval => {
|
match segment {
|
||||||
let string_intervals: Vec<_> = more_than_one_interval
|
(Unbounded, Unbounded) => write!(f, "*")?,
|
||||||
.iter()
|
(Unbounded, Included(v)) => write!(f, "<={v}")?,
|
||||||
.map(interval_to_string)
|
(Unbounded, Excluded(v)) => write!(f, "<{v}")?,
|
||||||
.collect();
|
(Included(v), Unbounded) => write!(f, ">={v}")?,
|
||||||
write!(f, "{}", string_intervals.join(" "))
|
(Included(v), Included(b)) => {
|
||||||
|
if v == b {
|
||||||
|
write!(f, "{v}")?
|
||||||
|
} else {
|
||||||
|
write!(f, ">={v},<={b}")?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Included(v), Excluded(b)) => write!(f, ">={v}, <{b}")?,
|
||||||
|
(Excluded(v), Unbounded) => write!(f, ">{v}")?,
|
||||||
|
(Excluded(v), Included(b)) => write!(f, ">{v}, <={b}")?,
|
||||||
|
(Excluded(v), Excluded(b)) => write!(f, ">{v}, <{b}")?,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interval_to_string<V: Version>((start, maybe_end): &Interval<V>) -> String {
|
// SERIALIZATION ###############################################################
|
||||||
match maybe_end {
|
|
||||||
Some(end) => format!("[ {}, {} [", start, end),
|
#[cfg(feature = "serde")]
|
||||||
None => format!("[ {}, ∞ [", start),
|
impl<'de, V: serde::Deserialize<'de>> serde::Deserialize<'de> for Range<V> {
|
||||||
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
// This enables conversion from the "old" discrete implementation of `Range` to the new
|
||||||
|
// bounded one.
|
||||||
|
//
|
||||||
|
// Serialization is always performed in the new format.
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum EitherInterval<V> {
|
||||||
|
B(Bound<V>, Bound<V>),
|
||||||
|
D(V, Option<V>),
|
||||||
|
}
|
||||||
|
|
||||||
|
let bounds: SmallVec<EitherInterval<V>> = serde::Deserialize::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
let mut segments = SmallVec::Empty;
|
||||||
|
for i in bounds {
|
||||||
|
match i {
|
||||||
|
EitherInterval::B(l, r) => segments.push((l, r)),
|
||||||
|
EitherInterval::D(l, Some(r)) => segments.push((Included(l), Excluded(r))),
|
||||||
|
EitherInterval::D(l, None) => segments.push((Included(l), Unbounded)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Range { segments })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,28 +435,74 @@ fn interval_to_string<V: Version>((start, maybe_end): &Interval<V>) -> String {
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use crate::version::NumberVersion;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn strategy() -> impl Strategy<Value = Range<NumberVersion>> {
|
/// Generate version sets from a random vector of deltas between bounds.
|
||||||
prop::collection::vec(any::<u32>(), 0..10).prop_map(|mut vec| {
|
/// Each bound is randomly inclusive or exclusive.
|
||||||
vec.sort_unstable();
|
pub fn strategy() -> impl Strategy<Value = Range<u32>> {
|
||||||
vec.dedup();
|
(
|
||||||
let mut pair_iter = vec.chunks_exact(2);
|
any::<bool>(),
|
||||||
let mut segments = SmallVec::empty();
|
prop::collection::vec(any::<(u32, bool)>(), 1..10),
|
||||||
while let Some([v1, v2]) = pair_iter.next() {
|
)
|
||||||
segments.push((NumberVersion(*v1), Some(NumberVersion(*v2))));
|
.prop_map(|(start_unbounded, deltas)| {
|
||||||
}
|
let mut start = if start_unbounded {
|
||||||
if let [v] = pair_iter.remainder() {
|
Some(Unbounded)
|
||||||
segments.push((NumberVersion(*v), None));
|
} else {
|
||||||
}
|
None
|
||||||
Range { segments }
|
};
|
||||||
})
|
let mut largest: u32 = 0;
|
||||||
|
let mut last_bound_was_inclusive = false;
|
||||||
|
let mut segments = SmallVec::Empty;
|
||||||
|
for (delta, inclusive) in deltas {
|
||||||
|
// Add the offset to the current bound
|
||||||
|
largest = match largest.checked_add(delta) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
// Skip this offset, if it would result in a too large bound.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_bound = if inclusive {
|
||||||
|
Included(largest)
|
||||||
|
} else {
|
||||||
|
Excluded(largest)
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we already have a start bound, the next offset defines the complete range.
|
||||||
|
// If we don't have a start bound, we have to generate one.
|
||||||
|
if let Some(start_bound) = start.take() {
|
||||||
|
// If the delta from the start bound is 0, the only authorized configuration is
|
||||||
|
// Included(x), Included(x)
|
||||||
|
if delta == 0 && !(matches!(start_bound, Included(_)) && inclusive) {
|
||||||
|
start = Some(start_bound);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_bound_was_inclusive = inclusive;
|
||||||
|
segments.push((start_bound, current_bound));
|
||||||
|
} else {
|
||||||
|
// If the delta from the end bound of the last range is 0 and
|
||||||
|
// any of the last ending or current starting bound is inclusive,
|
||||||
|
// we skip the delta because they basically overlap.
|
||||||
|
if delta == 0 && (last_bound_was_inclusive || inclusive) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
start = Some(current_bound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still have a start bound, but didn't have enough deltas to complete another
|
||||||
|
// segment, we add an unbounded upperbound.
|
||||||
|
if let Some(start_bound) = start {
|
||||||
|
segments.push((start_bound, Unbounded));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Range { segments }.check_invariants();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version_strat() -> impl Strategy<Value = NumberVersion> {
|
fn version_strat() -> impl Strategy<Value = u32> {
|
||||||
any::<u32>().prop_map(NumberVersion)
|
any::<u32>()
|
||||||
}
|
}
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
|
@ -327,17 +511,17 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate_is_different(range in strategy()) {
|
fn negate_is_different(range in strategy()) {
|
||||||
assert_ne!(range.negate(), range);
|
assert_ne!(range.complement(), range);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn double_negate_is_identity(range in strategy()) {
|
fn double_negate_is_identity(range in strategy()) {
|
||||||
assert_eq!(range.negate().negate(), range);
|
assert_eq!(range.complement().complement(), range);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negate_contains_opposite(range in strategy(), version in version_strat()) {
|
fn negate_contains_opposite(range in strategy(), version in version_strat()) {
|
||||||
assert_ne!(range.contains(&version), range.negate().contains(&version));
|
assert_ne!(range.contains(&version), range.complement().contains(&version));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Testing intersection ----------------------------
|
// Testing intersection ----------------------------
|
||||||
|
@ -349,12 +533,12 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn intersection_with_any_is_identity(range in strategy()) {
|
fn intersection_with_any_is_identity(range in strategy()) {
|
||||||
assert_eq!(Range::any().intersection(&range), range);
|
assert_eq!(Range::full().intersection(&range), range);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn intersection_with_none_is_none(range in strategy()) {
|
fn intersection_with_none_is_none(range in strategy()) {
|
||||||
assert_eq!(Range::none().intersection(&range), Range::none());
|
assert_eq!(Range::empty().intersection(&range), Range::empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -369,7 +553,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn intesection_of_complements_is_none(range in strategy()) {
|
fn intesection_of_complements_is_none(range in strategy()) {
|
||||||
assert_eq!(range.negate().intersection(&range), Range::none());
|
assert_eq!(range.complement().intersection(&range), Range::empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -381,7 +565,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn union_of_complements_is_any(range in strategy()) {
|
fn union_of_complements_is_any(range in strategy()) {
|
||||||
assert_eq!(range.negate().union(&range), Range::any());
|
assert_eq!(range.complement().union(&range), Range::full());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -393,17 +577,37 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn always_contains_exact(version in version_strat()) {
|
fn always_contains_exact(version in version_strat()) {
|
||||||
assert!(Range::exact(version).contains(&version));
|
assert!(Range::singleton(version).contains(&version));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn contains_negation(range in strategy(), version in version_strat()) {
|
fn contains_negation(range in strategy(), version in version_strat()) {
|
||||||
assert_ne!(range.contains(&version), range.negate().contains(&version));
|
assert_ne!(range.contains(&version), range.complement().contains(&version));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn contains_intersection(range in strategy(), version in version_strat()) {
|
fn contains_intersection(range in strategy(), version in version_strat()) {
|
||||||
assert_eq!(range.contains(&version), range.intersection(&Range::exact(version)) != Range::none());
|
assert_eq!(range.contains(&version), range.intersection(&Range::singleton(version)) != Range::empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contains_bounding_range(range in strategy(), version in version_strat()) {
|
||||||
|
if range.contains(&version) {
|
||||||
|
assert!(range.bounding_range().map(|b| b.contains(&version)).unwrap_or(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_range_bounds(range in any::<(Bound<u32>, Bound<u32>)>(), version in version_strat()) {
|
||||||
|
let rv: Range<_> = Range::from_range_bounds(range);
|
||||||
|
assert_eq!(range.contains(&version), rv.contains(&version));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_range_bounds_round_trip(range in any::<(Bound<u32>, Bound<u32>)>()) {
|
||||||
|
let rv: Range<u32> = Range::from_range_bounds(range);
|
||||||
|
let rv2: Range<u32> = rv.bounding_range().map(Range::from_range_bounds::<_, u32>).unwrap_or_else(Range::empty);
|
||||||
|
assert_eq!(rv, rv2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
156
vendor/pubgrub/src/report.rs
vendored
156
vendor/pubgrub/src/report.rs
vendored
|
@ -7,50 +7,49 @@ use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::package::Package;
|
use crate::package::Package;
|
||||||
use crate::range::Range;
|
|
||||||
use crate::term::Term;
|
use crate::term::Term;
|
||||||
use crate::type_aliases::Map;
|
use crate::type_aliases::Map;
|
||||||
use crate::version::Version;
|
use crate::version_set::VersionSet;
|
||||||
|
|
||||||
/// Reporter trait.
|
/// Reporter trait.
|
||||||
pub trait Reporter<P: Package, V: Version> {
|
pub trait Reporter<P: Package, VS: VersionSet> {
|
||||||
/// Output type of the report.
|
/// Output type of the report.
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
/// Generate a report from the derivation tree
|
/// Generate a report from the derivation tree
|
||||||
/// describing the resolution failure.
|
/// describing the resolution failure.
|
||||||
fn report(derivation_tree: &DerivationTree<P, V>) -> Self::Output;
|
fn report(derivation_tree: &DerivationTree<P, VS>) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derivation tree resulting in the impossibility
|
/// Derivation tree resulting in the impossibility
|
||||||
/// to solve the dependencies of our root package.
|
/// to solve the dependencies of our root package.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DerivationTree<P: Package, V: Version> {
|
pub enum DerivationTree<P: Package, VS: VersionSet> {
|
||||||
/// External incompatibility.
|
/// External incompatibility.
|
||||||
External(External<P, V>),
|
External(External<P, VS>),
|
||||||
/// Incompatibility derived from two others.
|
/// Incompatibility derived from two others.
|
||||||
Derived(Derived<P, V>),
|
Derived(Derived<P, VS>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Incompatibilities that are not derived from others,
|
/// Incompatibilities that are not derived from others,
|
||||||
/// they have their own reason.
|
/// they have their own reason.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum External<P: Package, V: Version> {
|
pub enum External<P: Package, VS: VersionSet> {
|
||||||
/// Initial incompatibility aiming at picking the root package for the first decision.
|
/// Initial incompatibility aiming at picking the root package for the first decision.
|
||||||
NotRoot(P, V),
|
NotRoot(P, VS::V),
|
||||||
/// There are no versions in the given range for this package.
|
/// There are no versions in the given set for this package.
|
||||||
NoVersions(P, Range<V>),
|
NoVersions(P, VS),
|
||||||
/// Dependencies of the package are unavailable for versions in that range.
|
/// Dependencies of the package are unavailable for versions in that set.
|
||||||
UnavailableDependencies(P, Range<V>),
|
UnavailableDependencies(P, VS),
|
||||||
/// Incompatibility coming from the dependencies of a given package.
|
/// Incompatibility coming from the dependencies of a given package.
|
||||||
FromDependencyOf(P, Range<V>, P, Range<V>),
|
FromDependencyOf(P, VS, P, VS),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Incompatibility derived from two others.
|
/// Incompatibility derived from two others.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Derived<P: Package, V: Version> {
|
pub struct Derived<P: Package, VS: VersionSet> {
|
||||||
/// Terms of the incompatibility.
|
/// Terms of the incompatibility.
|
||||||
pub terms: Map<P, Term<V>>,
|
pub terms: Map<P, Term<VS>>,
|
||||||
/// Indicate if that incompatibility is present multiple times
|
/// Indicate if that incompatibility is present multiple times
|
||||||
/// in the derivation tree.
|
/// in the derivation tree.
|
||||||
/// If that is the case, it has a unique id, provided in that option.
|
/// If that is the case, it has a unique id, provided in that option.
|
||||||
|
@ -58,12 +57,12 @@ pub struct Derived<P: Package, V: Version> {
|
||||||
/// and refer to the explanation for the other times.
|
/// and refer to the explanation for the other times.
|
||||||
pub shared_id: Option<usize>,
|
pub shared_id: Option<usize>,
|
||||||
/// First cause.
|
/// First cause.
|
||||||
pub cause1: Box<DerivationTree<P, V>>,
|
pub cause1: Box<DerivationTree<P, VS>>,
|
||||||
/// Second cause.
|
/// Second cause.
|
||||||
pub cause2: Box<DerivationTree<P, V>>,
|
pub cause2: Box<DerivationTree<P, VS>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> DerivationTree<P, V> {
|
impl<P: Package, VS: VersionSet> DerivationTree<P, VS> {
|
||||||
/// Merge the [NoVersions](External::NoVersions) external incompatibilities
|
/// Merge the [NoVersions](External::NoVersions) external incompatibilities
|
||||||
/// with the other one they are matched with
|
/// with the other one they are matched with
|
||||||
/// in a derived incompatibility.
|
/// in a derived incompatibility.
|
||||||
|
@ -100,7 +99,7 @@ impl<P: Package, V: Version> DerivationTree<P, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_no_versions(self, package: P, range: Range<V>) -> Option<Self> {
|
fn merge_no_versions(self, package: P, set: VS) -> Option<Self> {
|
||||||
match self {
|
match self {
|
||||||
// TODO: take care of the Derived case.
|
// TODO: take care of the Derived case.
|
||||||
// Once done, we can remove the Option.
|
// Once done, we can remove the Option.
|
||||||
|
@ -109,19 +108,16 @@ impl<P: Package, V: Version> DerivationTree<P, V> {
|
||||||
panic!("How did we end up with a NoVersions merged with a NotRoot?")
|
panic!("How did we end up with a NoVersions merged with a NotRoot?")
|
||||||
}
|
}
|
||||||
DerivationTree::External(External::NoVersions(_, r)) => Some(DerivationTree::External(
|
DerivationTree::External(External::NoVersions(_, r)) => Some(DerivationTree::External(
|
||||||
External::NoVersions(package, range.union(&r)),
|
External::NoVersions(package, set.union(&r)),
|
||||||
)),
|
)),
|
||||||
DerivationTree::External(External::UnavailableDependencies(_, r)) => {
|
DerivationTree::External(External::UnavailableDependencies(_, r)) => Some(
|
||||||
Some(DerivationTree::External(External::UnavailableDependencies(
|
DerivationTree::External(External::UnavailableDependencies(package, set.union(&r))),
|
||||||
package,
|
),
|
||||||
range.union(&r),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
DerivationTree::External(External::FromDependencyOf(p1, r1, p2, r2)) => {
|
DerivationTree::External(External::FromDependencyOf(p1, r1, p2, r2)) => {
|
||||||
if p1 == package {
|
if p1 == package {
|
||||||
Some(DerivationTree::External(External::FromDependencyOf(
|
Some(DerivationTree::External(External::FromDependencyOf(
|
||||||
p1,
|
p1,
|
||||||
r1.union(&range),
|
r1.union(&set),
|
||||||
p2,
|
p2,
|
||||||
r2,
|
r2,
|
||||||
)))
|
)))
|
||||||
|
@ -130,7 +126,7 @@ impl<P: Package, V: Version> DerivationTree<P, V> {
|
||||||
p1,
|
p1,
|
||||||
r1,
|
r1,
|
||||||
p2,
|
p2,
|
||||||
r2.union(&range),
|
r2.union(&set),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,39 +134,39 @@ impl<P: Package, V: Version> DerivationTree<P, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> fmt::Display for External<P, V> {
|
impl<P: Package, VS: VersionSet> fmt::Display for External<P, VS> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::NotRoot(package, version) => {
|
Self::NotRoot(package, version) => {
|
||||||
write!(f, "we are solving dependencies of {} {}", package, version)
|
write!(f, "we are solving dependencies of {} {}", package, version)
|
||||||
}
|
}
|
||||||
Self::NoVersions(package, range) => {
|
Self::NoVersions(package, set) => {
|
||||||
if range == &Range::any() {
|
if set == &VS::full() {
|
||||||
write!(f, "there is no available version for {}", package)
|
write!(f, "there is no available version for {}", package)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "there is no version of {} in {}", package, range)
|
write!(f, "there is no version of {} in {}", package, set)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::UnavailableDependencies(package, range) => {
|
Self::UnavailableDependencies(package, set) => {
|
||||||
if range == &Range::any() {
|
if set == &VS::full() {
|
||||||
write!(f, "dependencies of {} are unavailable", package)
|
write!(f, "dependencies of {} are unavailable", package)
|
||||||
} else {
|
} else {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"dependencies of {} at version {} are unavailable",
|
"dependencies of {} at version {} are unavailable",
|
||||||
package, range
|
package, set
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::FromDependencyOf(p, range_p, dep, range_dep) => {
|
Self::FromDependencyOf(p, set_p, dep, set_dep) => {
|
||||||
if range_p == &Range::any() && range_dep == &Range::any() {
|
if set_p == &VS::full() && set_dep == &VS::full() {
|
||||||
write!(f, "{} depends on {}", p, dep)
|
write!(f, "{} depends on {}", p, dep)
|
||||||
} else if range_p == &Range::any() {
|
} else if set_p == &VS::full() {
|
||||||
write!(f, "{} depends on {} {}", p, dep, range_dep)
|
write!(f, "{} depends on {} {}", p, dep, set_dep)
|
||||||
} else if range_dep == &Range::any() {
|
} else if set_dep == &VS::full() {
|
||||||
write!(f, "{} {} depends on {}", p, range_p, dep)
|
write!(f, "{} {} depends on {}", p, set_p, dep)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{} {} depends on {} {}", p, range_p, dep, range_dep)
|
write!(f, "{} {} depends on {} {}", p, set_p, dep, set_dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,17 +194,17 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_recursive<P: Package, V: Version>(&mut self, derived: &Derived<P, V>) {
|
fn build_recursive<P: Package, VS: VersionSet>(&mut self, derived: &Derived<P, VS>) {
|
||||||
self.build_recursive_helper(derived);
|
self.build_recursive_helper(derived);
|
||||||
if let Some(id) = derived.shared_id {
|
if let Some(id) = derived.shared_id {
|
||||||
if self.shared_with_ref.get(&id) == None {
|
if self.shared_with_ref.get(&id).is_none() {
|
||||||
self.add_line_ref();
|
self.add_line_ref();
|
||||||
self.shared_with_ref.insert(id, self.ref_count);
|
self.shared_with_ref.insert(id, self.ref_count);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_recursive_helper<P: Package, V: Version>(&mut self, current: &Derived<P, V>) {
|
fn build_recursive_helper<P: Package, VS: VersionSet>(&mut self, current: &Derived<P, VS>) {
|
||||||
match (current.cause1.deref(), current.cause2.deref()) {
|
match (current.cause1.deref(), current.cause2.deref()) {
|
||||||
(DerivationTree::External(external1), DerivationTree::External(external2)) => {
|
(DerivationTree::External(external1), DerivationTree::External(external2)) => {
|
||||||
// Simplest case, we just combine two external incompatibilities.
|
// Simplest case, we just combine two external incompatibilities.
|
||||||
|
@ -264,7 +260,7 @@ impl DefaultStringReporter {
|
||||||
// and finally conclude.
|
// and finally conclude.
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
self.build_recursive(derived1);
|
self.build_recursive(derived1);
|
||||||
if derived1.shared_id != None {
|
if derived1.shared_id.is_some() {
|
||||||
self.lines.push("".into());
|
self.lines.push("".into());
|
||||||
self.build_recursive(current);
|
self.build_recursive(current);
|
||||||
} else {
|
} else {
|
||||||
|
@ -285,11 +281,11 @@ impl DefaultStringReporter {
|
||||||
///
|
///
|
||||||
/// The result will depend on the fact that the derived incompatibility
|
/// The result will depend on the fact that the derived incompatibility
|
||||||
/// has already been explained or not.
|
/// has already been explained or not.
|
||||||
fn report_one_each<P: Package, V: Version>(
|
fn report_one_each<P: Package, VS: VersionSet>(
|
||||||
&mut self,
|
&mut self,
|
||||||
derived: &Derived<P, V>,
|
derived: &Derived<P, VS>,
|
||||||
external: &External<P, V>,
|
external: &External<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) {
|
) {
|
||||||
match self.line_ref_of(derived.shared_id) {
|
match self.line_ref_of(derived.shared_id) {
|
||||||
Some(ref_id) => self.lines.push(Self::explain_ref_and_external(
|
Some(ref_id) => self.lines.push(Self::explain_ref_and_external(
|
||||||
|
@ -303,11 +299,11 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report one derived (without a line ref yet) and one external.
|
/// Report one derived (without a line ref yet) and one external.
|
||||||
fn report_recurse_one_each<P: Package, V: Version>(
|
fn report_recurse_one_each<P: Package, VS: VersionSet>(
|
||||||
&mut self,
|
&mut self,
|
||||||
derived: &Derived<P, V>,
|
derived: &Derived<P, VS>,
|
||||||
external: &External<P, V>,
|
external: &External<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) {
|
) {
|
||||||
match (derived.cause1.deref(), derived.cause2.deref()) {
|
match (derived.cause1.deref(), derived.cause2.deref()) {
|
||||||
// If the derived cause has itself one external prior cause,
|
// If the derived cause has itself one external prior cause,
|
||||||
|
@ -341,10 +337,10 @@ impl DefaultStringReporter {
|
||||||
// String explanations #####################################################
|
// String explanations #####################################################
|
||||||
|
|
||||||
/// Simplest case, we just combine two external incompatibilities.
|
/// Simplest case, we just combine two external incompatibilities.
|
||||||
fn explain_both_external<P: Package, V: Version>(
|
fn explain_both_external<P: Package, VS: VersionSet>(
|
||||||
external1: &External<P, V>,
|
external1: &External<P, VS>,
|
||||||
external2: &External<P, V>,
|
external2: &External<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
// TODO: order should be chosen to make it more logical.
|
// TODO: order should be chosen to make it more logical.
|
||||||
format!(
|
format!(
|
||||||
|
@ -356,12 +352,12 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Both causes have already been explained so we use their refs.
|
/// Both causes have already been explained so we use their refs.
|
||||||
fn explain_both_ref<P: Package, V: Version>(
|
fn explain_both_ref<P: Package, VS: VersionSet>(
|
||||||
ref_id1: usize,
|
ref_id1: usize,
|
||||||
derived1: &Derived<P, V>,
|
derived1: &Derived<P, VS>,
|
||||||
ref_id2: usize,
|
ref_id2: usize,
|
||||||
derived2: &Derived<P, V>,
|
derived2: &Derived<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
// TODO: order should be chosen to make it more logical.
|
// TODO: order should be chosen to make it more logical.
|
||||||
format!(
|
format!(
|
||||||
|
@ -377,11 +373,11 @@ impl DefaultStringReporter {
|
||||||
/// One cause is derived (already explained so one-line),
|
/// One cause is derived (already explained so one-line),
|
||||||
/// the other is a one-line external cause,
|
/// the other is a one-line external cause,
|
||||||
/// and finally we conclude with the current incompatibility.
|
/// and finally we conclude with the current incompatibility.
|
||||||
fn explain_ref_and_external<P: Package, V: Version>(
|
fn explain_ref_and_external<P: Package, VS: VersionSet>(
|
||||||
ref_id: usize,
|
ref_id: usize,
|
||||||
derived: &Derived<P, V>,
|
derived: &Derived<P, VS>,
|
||||||
external: &External<P, V>,
|
external: &External<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
// TODO: order should be chosen to make it more logical.
|
// TODO: order should be chosen to make it more logical.
|
||||||
format!(
|
format!(
|
||||||
|
@ -394,9 +390,9 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an external cause to the chain of explanations.
|
/// Add an external cause to the chain of explanations.
|
||||||
fn and_explain_external<P: Package, V: Version>(
|
fn and_explain_external<P: Package, VS: VersionSet>(
|
||||||
external: &External<P, V>,
|
external: &External<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
format!(
|
format!(
|
||||||
"And because {}, {}.",
|
"And because {}, {}.",
|
||||||
|
@ -406,10 +402,10 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an already explained incompat to the chain of explanations.
|
/// Add an already explained incompat to the chain of explanations.
|
||||||
fn and_explain_ref<P: Package, V: Version>(
|
fn and_explain_ref<P: Package, VS: VersionSet>(
|
||||||
ref_id: usize,
|
ref_id: usize,
|
||||||
derived: &Derived<P, V>,
|
derived: &Derived<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
format!(
|
format!(
|
||||||
"And because {} ({}), {}.",
|
"And because {} ({}), {}.",
|
||||||
|
@ -420,10 +416,10 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an already explained incompat to the chain of explanations.
|
/// Add an already explained incompat to the chain of explanations.
|
||||||
fn and_explain_prior_and_external<P: Package, V: Version>(
|
fn and_explain_prior_and_external<P: Package, VS: VersionSet>(
|
||||||
prior_external: &External<P, V>,
|
prior_external: &External<P, VS>,
|
||||||
external: &External<P, V>,
|
external: &External<P, VS>,
|
||||||
current_terms: &Map<P, Term<V>>,
|
current_terms: &Map<P, Term<VS>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
format!(
|
format!(
|
||||||
"And because {} and {}, {}.",
|
"And because {} and {}, {}.",
|
||||||
|
@ -434,7 +430,7 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to print terms of an incompatibility in a human-readable way.
|
/// Try to print terms of an incompatibility in a human-readable way.
|
||||||
pub fn string_terms<P: Package, V: Version>(terms: &Map<P, Term<V>>) -> String {
|
pub fn string_terms<P: Package, VS: VersionSet>(terms: &Map<P, Term<VS>>) -> String {
|
||||||
let terms_vec: Vec<_> = terms.iter().collect();
|
let terms_vec: Vec<_> = terms.iter().collect();
|
||||||
match terms_vec.as_slice() {
|
match terms_vec.as_slice() {
|
||||||
[] => "version solving failed".into(),
|
[] => "version solving failed".into(),
|
||||||
|
@ -469,10 +465,10 @@ impl DefaultStringReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> Reporter<P, V> for DefaultStringReporter {
|
impl<P: Package, VS: VersionSet> Reporter<P, VS> for DefaultStringReporter {
|
||||||
type Output = String;
|
type Output = String;
|
||||||
|
|
||||||
fn report(derivation_tree: &DerivationTree<P, V>) -> Self::Output {
|
fn report(derivation_tree: &DerivationTree<P, VS>) -> Self::Output {
|
||||||
match derivation_tree {
|
match derivation_tree {
|
||||||
DerivationTree::External(external) => external.to_string(),
|
DerivationTree::External(external) => external.to_string(),
|
||||||
DerivationTree::Derived(derived) => {
|
DerivationTree::Derived(derived) => {
|
||||||
|
|
255
vendor/pubgrub/src/solver.rs
vendored
255
vendor/pubgrub/src/solver.rs
vendored
|
@ -27,8 +27,8 @@
|
||||||
//!
|
//!
|
||||||
//! The algorithm is generic and works for any type of dependency system
|
//! The algorithm is generic and works for any type of dependency system
|
||||||
//! as long as packages (P) and versions (V) implement
|
//! as long as packages (P) and versions (V) implement
|
||||||
//! the [Package](crate::package::Package) and [Version](crate::version::Version) traits.
|
//! the [Package] and [Version](crate::version::Version) traits.
|
||||||
//! [Package](crate::package::Package) is strictly equivalent and automatically generated
|
//! [Package] is strictly equivalent and automatically generated
|
||||||
//! for any type that implement [Clone] + [Eq] + [Hash] + [Debug] + [Display](std::fmt::Display).
|
//! for any type that implement [Clone] + [Eq] + [Hash] + [Debug] + [Display](std::fmt::Display).
|
||||||
//! [Version](crate::version::Version) simply states that versions are ordered,
|
//! [Version](crate::version::Version) simply states that versions are ordered,
|
||||||
//! that there should be
|
//! that there should be
|
||||||
|
@ -44,9 +44,12 @@
|
||||||
//! # use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
//! # use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
//! # use pubgrub::version::NumberVersion;
|
//! # use pubgrub::version::NumberVersion;
|
||||||
//! # use pubgrub::error::PubGrubError;
|
//! # use pubgrub::error::PubGrubError;
|
||||||
|
//! # use pubgrub::range::Range;
|
||||||
//! #
|
//! #
|
||||||
//! # fn try_main() -> Result<(), PubGrubError<&'static str, NumberVersion>> {
|
//! # type NumVS = Range<NumberVersion>;
|
||||||
//! # let dependency_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
|
//! #
|
||||||
|
//! # fn try_main() -> Result<(), PubGrubError<&'static str, NumVS>> {
|
||||||
|
//! # let dependency_provider = OfflineDependencyProvider::<&str, NumVS>::new();
|
||||||
//! # let package = "root";
|
//! # let package = "root";
|
||||||
//! # let version = 1;
|
//! # let version = 1;
|
||||||
//! let solution = resolve(&dependency_provider, package, version)?;
|
//! let solution = resolve(&dependency_provider, package, version)?;
|
||||||
|
@ -73,49 +76,56 @@ use crate::error::PubGrubError;
|
||||||
pub use crate::internal::core::State;
|
pub use crate::internal::core::State;
|
||||||
pub use crate::internal::incompatibility::Incompatibility;
|
pub use crate::internal::incompatibility::Incompatibility;
|
||||||
use crate::package::Package;
|
use crate::package::Package;
|
||||||
use crate::range::Range;
|
use crate::type_aliases::{DependencyConstraints, Map, SelectedDependencies};
|
||||||
use crate::type_aliases::{Map, SelectedDependencies};
|
use crate::version_set::VersionSet;
|
||||||
use crate::version::Version;
|
use log::{debug, info};
|
||||||
|
|
||||||
/// Main function of the library.
|
/// Main function of the library.
|
||||||
/// Finds a set of packages satisfying dependency bounds for a given package + version pair.
|
/// Finds a set of packages satisfying dependency bounds for a given package + version pair.
|
||||||
pub fn resolve<P: Package, V: Version>(
|
pub fn resolve<P: Package, VS: VersionSet>(
|
||||||
dependency_provider: &impl DependencyProvider<P, V>,
|
dependency_provider: &impl DependencyProvider<P, VS>,
|
||||||
package: P,
|
package: P,
|
||||||
version: impl Into<V>,
|
version: impl Into<VS::V>,
|
||||||
) -> Result<SelectedDependencies<P, V>, PubGrubError<P, V>> {
|
) -> Result<SelectedDependencies<P, VS::V>, PubGrubError<P, VS>> {
|
||||||
let mut state = State::init(package.clone(), version.into());
|
let mut state = State::init(package.clone(), version.into());
|
||||||
let mut added_dependencies: Map<P, Set<V>> = Map::default();
|
let mut added_dependencies: Map<P, Set<VS::V>> = Map::default();
|
||||||
let mut next = package;
|
let mut next = package;
|
||||||
loop {
|
loop {
|
||||||
dependency_provider
|
dependency_provider
|
||||||
.should_cancel()
|
.should_cancel()
|
||||||
.map_err(|err| PubGrubError::ErrorInShouldCancel(err))?;
|
.map_err(|err| PubGrubError::ErrorInShouldCancel(err))?;
|
||||||
|
|
||||||
|
info!("unit_propagation: {}", &next);
|
||||||
state.unit_propagation(next)?;
|
state.unit_propagation(next)?;
|
||||||
|
|
||||||
let potential_packages = state.partial_solution.potential_packages();
|
debug!(
|
||||||
if potential_packages.is_none() {
|
"Partial solution after unit propagation: {}",
|
||||||
drop(potential_packages);
|
state.partial_solution
|
||||||
// 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.
|
let Some(potential_packages) = state.partial_solution.potential_packages() else {
|
||||||
return state.partial_solution.extract_solution().ok_or_else(|| {
|
return state.partial_solution.extract_solution().ok_or_else(|| {
|
||||||
PubGrubError::Failure(
|
PubGrubError::Failure(
|
||||||
"How did we end up with no package to choose but no solution?".into(),
|
"How did we end up with no package to choose but no solution?".into(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
let decision = dependency_provider
|
let decision = dependency_provider
|
||||||
.choose_package_version(potential_packages.unwrap())
|
.choose_package_version(potential_packages)
|
||||||
.map_err(PubGrubError::ErrorChoosingPackageVersion)?;
|
.map_err(PubGrubError::ErrorChoosingPackageVersion)?;
|
||||||
|
info!("DP chose: {} @ {:?}", decision.0, decision.1);
|
||||||
|
|
||||||
next = decision.0.clone();
|
next = decision.0.clone();
|
||||||
|
|
||||||
// Pick the next compatible version.
|
// Pick the next compatible version.
|
||||||
let term_intersection = state
|
let term_intersection = state
|
||||||
.partial_solution
|
.partial_solution
|
||||||
.term_intersection_for_package(&next)
|
.term_intersection_for_package(&next)
|
||||||
.expect("a package was chosen but we don't have a term.");
|
.ok_or_else(|| {
|
||||||
|
PubGrubError::Failure("a package was chosen but we don't have a term.".into())
|
||||||
|
})?;
|
||||||
|
|
||||||
let v = match decision.1 {
|
let v = match decision.1 {
|
||||||
None => {
|
None => {
|
||||||
let inc = Incompatibility::no_versions(next.clone(), term_intersection.clone());
|
let inc = Incompatibility::no_versions(next.clone(), term_intersection.clone());
|
||||||
|
@ -124,108 +134,107 @@ pub fn resolve<P: Package, V: Version>(
|
||||||
}
|
}
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !term_intersection.contains(&v) {
|
if !term_intersection.contains(&v) {
|
||||||
return Err(PubGrubError::ErrorChoosingPackageVersion(
|
return Err(PubGrubError::ErrorChoosingPackageVersion(
|
||||||
"choose_package_version picked an incompatible version".into(),
|
"choose_package_version picked an incompatible version".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if added_dependencies
|
let is_new_dependency = added_dependencies
|
||||||
.entry(next.clone())
|
.entry(next.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(v.clone())
|
.insert(v.clone());
|
||||||
{
|
|
||||||
// Retrieve that package dependencies.
|
|
||||||
let p = &next;
|
|
||||||
let dependencies =
|
|
||||||
match dependency_provider
|
|
||||||
.get_dependencies(&p, &v)
|
|
||||||
.map_err(|err| PubGrubError::ErrorRetrievingDependencies {
|
|
||||||
package: p.clone(),
|
|
||||||
version: v.clone(),
|
|
||||||
source: err,
|
|
||||||
})? {
|
|
||||||
Dependencies::Unknown => {
|
|
||||||
state.add_incompatibility(Incompatibility::unavailable_dependencies(
|
|
||||||
p.clone(),
|
|
||||||
v.clone(),
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Dependencies::Known(x) => {
|
|
||||||
if x.contains_key(&p) {
|
|
||||||
return Err(PubGrubError::SelfDependency {
|
|
||||||
package: p.clone(),
|
|
||||||
version: v.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if let Some((dependent, _)) = x.iter().find(|(_, r)| r == &&Range::none()) {
|
|
||||||
return Err(PubGrubError::DependencyOnTheEmptySet {
|
|
||||||
package: p.clone(),
|
|
||||||
version: v.clone(),
|
|
||||||
dependent: dependent.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
x
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add that package and version if the dependencies are not problematic.
|
if !is_new_dependency {
|
||||||
let dep_incompats =
|
|
||||||
state.add_incompatibility_from_dependencies(p.clone(), v.clone(), &dependencies);
|
|
||||||
|
|
||||||
// TODO: I don't think this check can actually happen.
|
|
||||||
// We might want to put it under #[cfg(debug_assertions)].
|
|
||||||
if state.incompatibility_store[dep_incompats.clone()]
|
|
||||||
.iter()
|
|
||||||
.any(|incompat| state.is_terminal(incompat))
|
|
||||||
{
|
|
||||||
// For a dependency incompatibility to be terminal,
|
|
||||||
// it can only mean that root depend on not root?
|
|
||||||
return Err(PubGrubError::Failure(
|
|
||||||
"Root package depends on itself at a different version?".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
state.partial_solution.add_version(
|
|
||||||
p.clone(),
|
|
||||||
v,
|
|
||||||
dep_incompats,
|
|
||||||
&state.incompatibility_store,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// `dep_incompats` are already in `incompatibilities` so we know there are not satisfied
|
// `dep_incompats` are already in `incompatibilities` so we know there are not satisfied
|
||||||
// terms and can add the decision directly.
|
// terms and can add the decision directly.
|
||||||
|
info!("add_decision (not first time): {} @ {}", &next, v);
|
||||||
state.partial_solution.add_decision(next.clone(), v);
|
state.partial_solution.add_decision(next.clone(), v);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve that package dependencies.
|
||||||
|
let p = &next;
|
||||||
|
let dependencies = dependency_provider.get_dependencies(p, &v).map_err(|err| {
|
||||||
|
PubGrubError::ErrorRetrievingDependencies {
|
||||||
|
package: p.clone(),
|
||||||
|
version: v.clone(),
|
||||||
|
source: err,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let known_dependencies = match dependencies {
|
||||||
|
Dependencies::Unknown => {
|
||||||
|
state.add_incompatibility(Incompatibility::unavailable_dependencies(
|
||||||
|
p.clone(),
|
||||||
|
v.clone(),
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Dependencies::Known(x) if x.contains_key(p) => {
|
||||||
|
return Err(PubGrubError::SelfDependency {
|
||||||
|
package: p.clone(),
|
||||||
|
version: v.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Dependencies::Known(x) => {
|
||||||
|
if let Some((dependent, _)) = x.iter().find(|(_, r)| r == &&VS::empty()) {
|
||||||
|
return Err(PubGrubError::DependencyOnTheEmptySet {
|
||||||
|
package: p.clone(),
|
||||||
|
version: v.clone(),
|
||||||
|
dependent: dependent.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
x
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add that package and version if the dependencies are not problematic.
|
||||||
|
let dep_incompats =
|
||||||
|
state.add_incompatibility_from_dependencies(p.clone(), v.clone(), &known_dependencies);
|
||||||
|
|
||||||
|
// TODO: I don't think this check can actually happen.
|
||||||
|
// We might want to put it under #[cfg(debug_assertions)].
|
||||||
|
let incompatible_self_dependency = state.incompatibility_store[dep_incompats.clone()]
|
||||||
|
.iter()
|
||||||
|
.any(|incompat| state.is_terminal(incompat));
|
||||||
|
if incompatible_self_dependency {
|
||||||
|
// For a dependency incompatibility to be terminal,
|
||||||
|
// it can only mean that root depend on not root?
|
||||||
|
return Err(PubGrubError::Failure(
|
||||||
|
"Root package depends on itself at a different version?".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.partial_solution.add_version(
|
||||||
|
p.clone(),
|
||||||
|
v,
|
||||||
|
dep_incompats,
|
||||||
|
&state.incompatibility_store,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An enum used by [DependencyProvider] that holds information about package dependencies.
|
/// An enum used by [DependencyProvider] that holds information about package dependencies.
|
||||||
/// For each [Package] there is a [Range] of concrete versions it allows as a dependency.
|
/// For each [Package] there is a set of versions allowed as a dependency.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Dependencies<P: Package, V: Version> {
|
pub enum Dependencies<P: Package, VS: VersionSet> {
|
||||||
/// Package dependencies are unavailable.
|
/// Package dependencies are unavailable.
|
||||||
Unknown,
|
Unknown,
|
||||||
/// Container for all available package versions.
|
/// Container for all available package versions.
|
||||||
Known(DependencyConstraints<P, V>),
|
Known(DependencyConstraints<P, VS>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtype of [Dependencies] which holds information about
|
|
||||||
/// all possible versions a given package can accept.
|
|
||||||
/// There is a difference in semantics between an empty [Map<P, Range<V>>](crate::type_aliases::Map)
|
|
||||||
/// inside [DependencyConstraints] and [Dependencies::Unknown]:
|
|
||||||
/// the former means the package has no dependencies and it is a known fact,
|
|
||||||
/// while the latter means they could not be fetched by [DependencyProvider].
|
|
||||||
pub type DependencyConstraints<P, V> = Map<P, Range<V>>;
|
|
||||||
|
|
||||||
/// Trait that allows the algorithm to retrieve available packages and their dependencies.
|
/// Trait that allows the algorithm to retrieve available packages and their dependencies.
|
||||||
/// An implementor needs to be supplied to the [resolve] function.
|
/// An implementor needs to be supplied to the [resolve] function.
|
||||||
pub trait DependencyProvider<P: Package, V: Version> {
|
pub trait DependencyProvider<P: Package, VS: VersionSet> {
|
||||||
/// [Decision making](https://github.com/dart-lang/pub/blob/master/doc/solver.md#decision-making)
|
/// [Decision making](https://github.com/dart-lang/pub/blob/master/doc/solver.md#decision-making)
|
||||||
/// is the process of choosing the next package
|
/// is the process of choosing the next package
|
||||||
/// and version that will be appended to the partial solution.
|
/// and version that will be appended to the partial solution.
|
||||||
/// Every time such a decision must be made,
|
/// Every time such a decision must be made,
|
||||||
/// potential valid packages and version ranges are preselected by the resolver,
|
/// potential valid packages and sets of versions are preselected by the resolver,
|
||||||
/// and the dependency provider must choose.
|
/// and the dependency provider must choose.
|
||||||
///
|
///
|
||||||
/// The strategy employed to choose such package and version
|
/// The strategy employed to choose such package and version
|
||||||
|
@ -246,18 +255,19 @@ pub trait DependencyProvider<P: Package, V: Version> {
|
||||||
/// of the available versions in preference order for any package.
|
/// of the available versions in preference order for any package.
|
||||||
///
|
///
|
||||||
/// Note: the type `T` ensures that this returns an item from the `packages` argument.
|
/// Note: the type `T` ensures that this returns an item from the `packages` argument.
|
||||||
fn choose_package_version<T: Borrow<P>, U: Borrow<Range<V>>>(
|
#[allow(clippy::type_complexity)]
|
||||||
|
fn choose_package_version<T: Borrow<P>, U: Borrow<VS>>(
|
||||||
&self,
|
&self,
|
||||||
potential_packages: impl Iterator<Item = (T, U)>,
|
potential_packages: impl Iterator<Item = (T, U)>,
|
||||||
) -> Result<(T, Option<V>), Box<dyn Error + Send + Sync>>;
|
) -> Result<(T, Option<VS::V>), Box<dyn Error + Send + Sync>>;
|
||||||
|
|
||||||
/// Retrieves the package dependencies.
|
/// Retrieves the package dependencies.
|
||||||
/// Return [Dependencies::Unknown] if its dependencies are unknown.
|
/// Return [Dependencies::Unknown] if its dependencies are unknown.
|
||||||
fn get_dependencies(
|
fn get_dependencies(
|
||||||
&self,
|
&self,
|
||||||
package: &P,
|
package: &P,
|
||||||
version: &V,
|
version: &VS::V,
|
||||||
) -> Result<Dependencies<P, V>, Box<dyn Error + Send + Sync>>;
|
) -> Result<Dependencies<P, VS>, Box<dyn Error + Send + Sync>>;
|
||||||
|
|
||||||
/// This is called fairly regularly during the resolution,
|
/// This is called fairly regularly during the resolution,
|
||||||
/// if it returns an Err then resolution will be terminated.
|
/// if it returns an Err then resolution will be terminated.
|
||||||
|
@ -276,38 +286,44 @@ pub trait DependencyProvider<P: Package, V: Version> {
|
||||||
/// The helper finds the package from the `packages` argument with the fewest versions from
|
/// 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
|
/// `list_available_versions` contained in the constraints. Then takes that package and finds the
|
||||||
/// first version contained in the constraints.
|
/// first version contained in the constraints.
|
||||||
pub fn choose_package_with_fewest_versions<P: Package, V: Version, T, U, I, F>(
|
pub fn choose_package_with_fewest_versions<P: Package, VS: VersionSet, T, U, I, F>(
|
||||||
list_available_versions: F,
|
list_available_versions: F,
|
||||||
potential_packages: impl Iterator<Item = (T, U)>,
|
potential_packages: impl Iterator<Item = (T, U)>,
|
||||||
) -> (T, Option<V>)
|
) -> (T, Option<VS::V>)
|
||||||
where
|
where
|
||||||
T: Borrow<P>,
|
T: Borrow<P>,
|
||||||
U: Borrow<Range<V>>,
|
U: Borrow<VS>,
|
||||||
I: Iterator<Item = V>,
|
I: Iterator<Item = VS::V>,
|
||||||
F: Fn(&P) -> I,
|
F: Fn(&P) -> I,
|
||||||
{
|
{
|
||||||
let count_valid = |(p, range): &(T, U)| {
|
let count_valid = |(p, set): &(T, U)| {
|
||||||
list_available_versions(p.borrow())
|
list_available_versions(p.borrow())
|
||||||
.filter(|v| range.borrow().contains(v.borrow()))
|
.filter(|v| set.borrow().contains(v))
|
||||||
.count()
|
.count()
|
||||||
};
|
};
|
||||||
let (pkg, range) = potential_packages
|
let (pkg, set) = potential_packages
|
||||||
.min_by_key(count_valid)
|
.min_by_key(count_valid)
|
||||||
.expect("potential_packages gave us an empty iterator");
|
.expect("potential_packages gave us an empty iterator");
|
||||||
let version =
|
let version = list_available_versions(pkg.borrow()).find(|v| set.borrow().contains(v));
|
||||||
list_available_versions(pkg.borrow()).find(|v| range.borrow().contains(v.borrow()));
|
|
||||||
(pkg, version)
|
(pkg, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A basic implementation of [DependencyProvider].
|
/// A basic implementation of [DependencyProvider].
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(bound(
|
||||||
|
serialize = "VS::V: serde::Serialize, VS: serde::Serialize, P: serde::Serialize",
|
||||||
|
deserialize = "VS::V: serde::Deserialize<'de>, VS: serde::Deserialize<'de>, P: serde::Deserialize<'de>"
|
||||||
|
))
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
pub struct OfflineDependencyProvider<P: Package, V: Version> {
|
pub struct OfflineDependencyProvider<P: Package, VS: VersionSet> {
|
||||||
dependencies: Map<P, BTreeMap<V, DependencyConstraints<P, V>>>,
|
dependencies: Map<P, BTreeMap<VS::V, DependencyConstraints<P, VS>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> OfflineDependencyProvider<P, V> {
|
impl<P: Package, VS: VersionSet> OfflineDependencyProvider<P, VS> {
|
||||||
/// Creates an empty OfflineDependencyProvider with no dependencies.
|
/// Creates an empty OfflineDependencyProvider with no dependencies.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -325,10 +341,10 @@ impl<P: Package, V: Version> OfflineDependencyProvider<P, V> {
|
||||||
/// The API does not allow to add dependencies one at a time to uphold an assumption that
|
/// The API does not allow to add dependencies one at a time to uphold an assumption that
|
||||||
/// [OfflineDependencyProvider.get_dependencies(p, v)](OfflineDependencyProvider::get_dependencies)
|
/// [OfflineDependencyProvider.get_dependencies(p, v)](OfflineDependencyProvider::get_dependencies)
|
||||||
/// provides all dependencies of a given package (p) and version (v) pair.
|
/// provides all dependencies of a given package (p) and version (v) pair.
|
||||||
pub fn add_dependencies<I: IntoIterator<Item = (P, Range<V>)>>(
|
pub fn add_dependencies<I: IntoIterator<Item = (P, VS)>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
package: P,
|
package: P,
|
||||||
version: impl Into<V>,
|
version: impl Into<VS::V>,
|
||||||
dependencies: I,
|
dependencies: I,
|
||||||
) {
|
) {
|
||||||
let package_deps = dependencies.into_iter().collect();
|
let package_deps = dependencies.into_iter().collect();
|
||||||
|
@ -348,13 +364,13 @@ impl<P: Package, V: Version> OfflineDependencyProvider<P, V> {
|
||||||
|
|
||||||
/// Lists versions of saved packages in sorted order.
|
/// Lists versions of saved packages in sorted order.
|
||||||
/// Returns [None] if no information is available regarding that package.
|
/// Returns [None] if no information is available regarding that package.
|
||||||
pub fn versions(&self, package: &P) -> Option<impl Iterator<Item = &V>> {
|
pub fn versions(&self, package: &P) -> Option<impl Iterator<Item = &VS::V>> {
|
||||||
self.dependencies.get(package).map(|k| k.keys())
|
self.dependencies.get(package).map(|k| k.keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lists dependencies of a given package and version.
|
/// Lists dependencies of a given package and version.
|
||||||
/// Returns [None] if no information is available regarding that package and version pair.
|
/// Returns [None] if no information is available regarding that package and version pair.
|
||||||
fn dependencies(&self, package: &P, version: &V) -> Option<DependencyConstraints<P, V>> {
|
fn dependencies(&self, package: &P, version: &VS::V) -> Option<DependencyConstraints<P, VS>> {
|
||||||
self.dependencies.get(package)?.get(version).cloned()
|
self.dependencies.get(package)?.get(version).cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,11 +379,12 @@ impl<P: Package, V: Version> OfflineDependencyProvider<P, V> {
|
||||||
/// contains all dependency information available in memory.
|
/// contains all dependency information available in memory.
|
||||||
/// Packages are picked with the fewest versions contained in the constraints first.
|
/// Packages are picked with the fewest versions contained in the constraints first.
|
||||||
/// Versions are picked with the newest versions first.
|
/// Versions are picked with the newest versions first.
|
||||||
impl<P: Package, V: Version> DependencyProvider<P, V> for OfflineDependencyProvider<P, V> {
|
impl<P: Package, VS: VersionSet> DependencyProvider<P, VS> for OfflineDependencyProvider<P, VS> {
|
||||||
fn choose_package_version<T: Borrow<P>, U: Borrow<Range<V>>>(
|
#[allow(clippy::type_complexity)]
|
||||||
|
fn choose_package_version<T: Borrow<P>, U: Borrow<VS>>(
|
||||||
&self,
|
&self,
|
||||||
potential_packages: impl Iterator<Item = (T, U)>,
|
potential_packages: impl Iterator<Item = (T, U)>,
|
||||||
) -> Result<(T, Option<V>), Box<dyn Error + Send + Sync>> {
|
) -> Result<(T, Option<VS::V>), Box<dyn Error + Send + Sync>> {
|
||||||
Ok(choose_package_with_fewest_versions(
|
Ok(choose_package_with_fewest_versions(
|
||||||
|p| {
|
|p| {
|
||||||
self.dependencies
|
self.dependencies
|
||||||
|
@ -384,8 +401,8 @@ impl<P: Package, V: Version> DependencyProvider<P, V> for OfflineDependencyProvi
|
||||||
fn get_dependencies(
|
fn get_dependencies(
|
||||||
&self,
|
&self,
|
||||||
package: &P,
|
package: &P,
|
||||||
version: &V,
|
version: &VS::V,
|
||||||
) -> Result<Dependencies<P, V>, Box<dyn Error + Send + Sync>> {
|
) -> Result<Dependencies<P, VS>, Box<dyn Error + Send + Sync>> {
|
||||||
Ok(match self.dependencies(package, version) {
|
Ok(match self.dependencies(package, version) {
|
||||||
None => Dependencies::Unknown,
|
None => Dependencies::Unknown,
|
||||||
Some(dependencies) => Dependencies::Known(dependencies),
|
Some(dependencies) => Dependencies::Known(dependencies),
|
||||||
|
|
77
vendor/pubgrub/src/term.rs
vendored
77
vendor/pubgrub/src/term.rs
vendored
|
@ -3,38 +3,37 @@
|
||||||
//! A term is the fundamental unit of operation of the PubGrub algorithm.
|
//! A term is the fundamental unit of operation of the PubGrub algorithm.
|
||||||
//! It is a positive or negative expression regarding a set of versions.
|
//! It is a positive or negative expression regarding a set of versions.
|
||||||
|
|
||||||
use crate::range::Range;
|
use crate::version_set::VersionSet;
|
||||||
use crate::version::Version;
|
use std::fmt::{self, Display};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
/// A positive or negative expression regarding a set of versions.
|
/// A positive or negative expression regarding a set of versions.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum Term<V: Version> {
|
pub enum Term<VS: VersionSet> {
|
||||||
/// For example, "1.0.0 <= v < 2.0.0" is a positive expression
|
/// For example, "1.0.0 <= v < 2.0.0" is a positive expression
|
||||||
/// that is evaluated true if a version is selected
|
/// that is evaluated true if a version is selected
|
||||||
/// and comprised between version 1.0.0 and version 2.0.0.
|
/// and comprised between version 1.0.0 and version 2.0.0.
|
||||||
Positive(Range<V>),
|
Positive(VS),
|
||||||
/// The term "not v < 3.0.0" is a negative expression
|
/// The term "not v < 3.0.0" is a negative expression
|
||||||
/// that is evaluated true if a version is selected >= 3.0.0
|
/// that is evaluated true if a version is selected >= 3.0.0
|
||||||
/// or if no version is selected at all.
|
/// or if no version is selected at all.
|
||||||
Negative(Range<V>),
|
Negative(VS),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Base methods.
|
/// Base methods.
|
||||||
impl<V: Version> Term<V> {
|
impl<VS: VersionSet> Term<VS> {
|
||||||
/// A term that is always true.
|
/// A term that is always true.
|
||||||
pub(crate) fn any() -> Self {
|
pub(crate) fn any() -> Self {
|
||||||
Self::Negative(Range::none())
|
Self::Negative(VS::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A term that is never true.
|
/// A term that is never true.
|
||||||
pub(crate) fn empty() -> Self {
|
pub(crate) fn empty() -> Self {
|
||||||
Self::Positive(Range::none())
|
Self::Positive(VS::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A positive term containing exactly that version.
|
/// A positive term containing exactly that version.
|
||||||
pub(crate) fn exact(version: V) -> Self {
|
pub(crate) fn exact(version: VS::V) -> Self {
|
||||||
Self::Positive(Range::exact(version))
|
Self::Positive(VS::singleton(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simply check if a term is positive.
|
/// Simply check if a term is positive.
|
||||||
|
@ -50,41 +49,41 @@ impl<V: Version> Term<V> {
|
||||||
/// the opposite of the evaluation of the original one.
|
/// the opposite of the evaluation of the original one.
|
||||||
pub(crate) fn negate(&self) -> Self {
|
pub(crate) fn negate(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Positive(range) => Self::Negative(range.clone()),
|
Self::Positive(set) => Self::Negative(set.clone()),
|
||||||
Self::Negative(range) => Self::Positive(range.clone()),
|
Self::Negative(set) => Self::Positive(set.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a term regarding a given choice of version.
|
/// Evaluate a term regarding a given choice of version.
|
||||||
pub(crate) fn contains(&self, v: &V) -> bool {
|
pub(crate) fn contains(&self, v: &VS::V) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Positive(range) => range.contains(v),
|
Self::Positive(set) => set.contains(v),
|
||||||
Self::Negative(range) => !(range.contains(v)),
|
Self::Negative(set) => !(set.contains(v)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unwrap the range contains in a positive term.
|
/// Unwrap the set contained in a positive term.
|
||||||
/// Will panic if used on a negative range.
|
/// Will panic if used on a negative set.
|
||||||
pub(crate) fn unwrap_positive(&self) -> &Range<V> {
|
pub(crate) fn unwrap_positive(&self) -> &VS {
|
||||||
match self {
|
match self {
|
||||||
Self::Positive(range) => range,
|
Self::Positive(set) => set,
|
||||||
_ => panic!("Negative term cannot unwrap positive range"),
|
_ => panic!("Negative term cannot unwrap positive set"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set operations with terms.
|
/// Set operations with terms.
|
||||||
impl<V: Version> Term<V> {
|
impl<VS: VersionSet> Term<VS> {
|
||||||
/// Compute the intersection of two terms.
|
/// Compute the intersection of two terms.
|
||||||
/// If at least one term is positive, the intersection is also positive.
|
/// If at least one term is positive, the intersection is also positive.
|
||||||
pub(crate) fn intersection(&self, other: &Term<V>) -> Term<V> {
|
pub(crate) fn intersection(&self, other: &Self) -> Self {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Positive(r1), Self::Positive(r2)) => Self::Positive(r1.intersection(r2)),
|
(Self::Positive(r1), Self::Positive(r2)) => Self::Positive(r1.intersection(r2)),
|
||||||
(Self::Positive(r1), Self::Negative(r2)) => {
|
(Self::Positive(r1), Self::Negative(r2)) => {
|
||||||
Self::Positive(r1.intersection(&r2.negate()))
|
Self::Positive(r1.intersection(&r2.complement()))
|
||||||
}
|
}
|
||||||
(Self::Negative(r1), Self::Positive(r2)) => {
|
(Self::Negative(r1), Self::Positive(r2)) => {
|
||||||
Self::Positive(r1.negate().intersection(r2))
|
Self::Positive(r1.complement().intersection(r2))
|
||||||
}
|
}
|
||||||
(Self::Negative(r1), Self::Negative(r2)) => Self::Negative(r1.union(r2)),
|
(Self::Negative(r1), Self::Negative(r2)) => Self::Negative(r1.union(r2)),
|
||||||
}
|
}
|
||||||
|
@ -92,14 +91,14 @@ impl<V: Version> Term<V> {
|
||||||
|
|
||||||
/// Compute the union of two terms.
|
/// Compute the union of two terms.
|
||||||
/// If at least one term is negative, the union is also negative.
|
/// If at least one term is negative, the union is also negative.
|
||||||
pub(crate) fn union(&self, other: &Term<V>) -> Term<V> {
|
pub(crate) fn union(&self, other: &Self) -> Self {
|
||||||
(self.negate().intersection(&other.negate())).negate()
|
(self.negate().intersection(&other.negate())).negate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicate if this term is a subset of another term.
|
/// Indicate if this term is a subset of another term.
|
||||||
/// Just like for sets, we say that t1 is a subset of t2
|
/// Just like for sets, we say that t1 is a subset of t2
|
||||||
/// if and only if t1 ∩ t2 = t1.
|
/// if and only if t1 ∩ t2 = t1.
|
||||||
pub(crate) fn subset_of(&self, other: &Term<V>) -> bool {
|
pub(crate) fn subset_of(&self, other: &Self) -> bool {
|
||||||
self == &self.intersection(other)
|
self == &self.intersection(other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +119,7 @@ pub(crate) enum Relation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relation between terms.
|
/// Relation between terms.
|
||||||
impl<'a, V: 'a + Version> Term<V> {
|
impl<VS: VersionSet> Term<VS> {
|
||||||
/// Check if a set of terms satisfies this term.
|
/// Check if a set of terms satisfies this term.
|
||||||
///
|
///
|
||||||
/// We say that a set of terms S "satisfies" a term t
|
/// We say that a set of terms S "satisfies" a term t
|
||||||
|
@ -129,7 +128,7 @@ impl<'a, V: 'a + Version> Term<V> {
|
||||||
/// It turns out that this can also be expressed with set operations:
|
/// It turns out that this can also be expressed with set operations:
|
||||||
/// S satisfies t if and only if ⋂ S ⊆ t
|
/// S satisfies t if and only if ⋂ S ⊆ t
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn satisfied_by(&self, terms_intersection: &Term<V>) -> bool {
|
fn satisfied_by(&self, terms_intersection: &Self) -> bool {
|
||||||
terms_intersection.subset_of(self)
|
terms_intersection.subset_of(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,13 +141,13 @@ impl<'a, V: 'a + Version> Term<V> {
|
||||||
/// S contradicts t if and only if ⋂ S is disjoint with t
|
/// S contradicts t if and only if ⋂ S is disjoint with t
|
||||||
/// S contradicts t if and only if (⋂ S) ⋂ t = ∅
|
/// S contradicts t if and only if (⋂ S) ⋂ t = ∅
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn contradicted_by(&self, terms_intersection: &Term<V>) -> bool {
|
fn contradicted_by(&self, terms_intersection: &Self) -> bool {
|
||||||
terms_intersection.intersection(self) == Self::empty()
|
terms_intersection.intersection(self) == Self::empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a set of terms satisfies or contradicts a given term.
|
/// Check if a set of terms satisfies or contradicts a given term.
|
||||||
/// Otherwise the relation is inconclusive.
|
/// Otherwise the relation is inconclusive.
|
||||||
pub(crate) fn relation_with(&self, other_terms_intersection: &Term<V>) -> Relation {
|
pub(crate) fn relation_with(&self, other_terms_intersection: &Self) -> Relation {
|
||||||
let full_intersection = self.intersection(other_terms_intersection);
|
let full_intersection = self.intersection(other_terms_intersection);
|
||||||
if &full_intersection == other_terms_intersection {
|
if &full_intersection == other_terms_intersection {
|
||||||
Relation::Satisfied
|
Relation::Satisfied
|
||||||
|
@ -160,19 +159,19 @@ impl<'a, V: 'a + Version> Term<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Version> AsRef<Term<V>> for Term<V> {
|
impl<VS: VersionSet> AsRef<Self> for Term<VS> {
|
||||||
fn as_ref(&self) -> &Term<V> {
|
fn as_ref(&self) -> &Self {
|
||||||
&self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// REPORT ######################################################################
|
// REPORT ######################################################################
|
||||||
|
|
||||||
impl<V: Version + fmt::Display> fmt::Display for Term<V> {
|
impl<VS: VersionSet + Display> Display for Term<VS> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Positive(range) => write!(f, "{}", range),
|
Self::Positive(set) => write!(f, "{}", set),
|
||||||
Self::Negative(range) => write!(f, "Not ( {} )", range),
|
Self::Negative(set) => write!(f, "Not ( {} )", set),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,10 +181,10 @@ impl<V: Version + fmt::Display> fmt::Display for Term<V> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::version::NumberVersion;
|
use crate::range::Range;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
pub fn strategy() -> impl Strategy<Value = Term<NumberVersion>> {
|
pub fn strategy() -> impl Strategy<Value = Term<Range<u32>>> {
|
||||||
prop_oneof
|
/// Concrete dependencies picked by the library during [resolve](crate::solver::resolve)
|
||||||
/// from [DependencyConstraints](crate::solver::DependencyConstraints)
|
/// from [DependencyConstraints].
|
||||||
pub type SelectedDependencies<P, V> = Map<P, V>;
|
pub type SelectedDependencies<P, V> = Map<P, V>;
|
||||||
|
|
||||||
|
/// Holds information about all possible versions a given package can accept.
|
||||||
|
/// There is a difference in semantics between an empty map
|
||||||
|
/// inside [DependencyConstraints] and [Dependencies::Unknown](crate::solver::Dependencies::Unknown):
|
||||||
|
/// the former means the package has no dependency and it is a known fact,
|
||||||
|
/// while the latter means they could not be fetched by the [DependencyProvider](crate::solver::DependencyProvider).
|
||||||
|
pub type DependencyConstraints<P, VS> = Map<P, VS>;
|
||||||
|
|
29
vendor/pubgrub/src/version.rs
vendored
29
vendor/pubgrub/src/version.rs
vendored
|
@ -80,6 +80,21 @@ impl From<(u32, u32, u32)> for SemanticVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a &(major, minor, patch) into a version.
|
||||||
|
impl From<&(u32, u32, u32)> for SemanticVersion {
|
||||||
|
fn from(tuple: &(u32, u32, u32)) -> Self {
|
||||||
|
let (major, minor, patch) = *tuple;
|
||||||
|
Self::new(major, minor, patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an &version into a version.
|
||||||
|
impl From<&SemanticVersion> for SemanticVersion {
|
||||||
|
fn from(v: &SemanticVersion) -> Self {
|
||||||
|
*v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert a version into a tuple (major, minor, patch).
|
// Convert a version into a tuple (major, minor, patch).
|
||||||
impl From<SemanticVersion> for (u32, u32, u32) {
|
impl From<SemanticVersion> for (u32, u32, u32) {
|
||||||
fn from(v: SemanticVersion) -> Self {
|
fn from(v: SemanticVersion) -> Self {
|
||||||
|
@ -237,6 +252,20 @@ impl From<u32> for NumberVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert an &usize into a version.
|
||||||
|
impl From<&u32> for NumberVersion {
|
||||||
|
fn from(v: &u32) -> Self {
|
||||||
|
Self(*v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an &version into a version.
|
||||||
|
impl From<&NumberVersion> for NumberVersion {
|
||||||
|
fn from(v: &NumberVersion) -> Self {
|
||||||
|
*v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert a version into an usize.
|
// Convert a version into an usize.
|
||||||
impl From<NumberVersion> for u32 {
|
impl From<NumberVersion> for u32 {
|
||||||
fn from(version: NumberVersion) -> Self {
|
fn from(version: NumberVersion) -> Self {
|
||||||
|
|
60
vendor/pubgrub/src/version_set.rs
vendored
Normal file
60
vendor/pubgrub/src/version_set.rs
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! As its name suggests, the [VersionSet] trait describes sets of versions.
|
||||||
|
//!
|
||||||
|
//! One needs to define
|
||||||
|
//! - the associate type for versions,
|
||||||
|
//! - two constructors for the empty set and a singleton set,
|
||||||
|
//! - the complement and intersection set operations,
|
||||||
|
//! - and a function to evaluate membership of versions.
|
||||||
|
//!
|
||||||
|
//! Two functions are automatically derived, thanks to the mathematical properties of sets.
|
||||||
|
//! You can overwrite those implementations, but we highly recommend that you don't,
|
||||||
|
//! except if you are confident in a correct implementation that brings much performance gains.
|
||||||
|
//!
|
||||||
|
//! It is also extremely important that the `Eq` trait is correctly implemented.
|
||||||
|
//! In particular, you can only use `#[derive(Eq, PartialEq)]` if `Eq` is strictly equivalent to the
|
||||||
|
//! structural equality, i.e. if version sets have canonical representations.
|
||||||
|
//! Such problems may arise if your implementations of `complement()` and `intersection()` do not
|
||||||
|
//! return canonical representations so be careful there.
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
|
/// Trait describing sets of versions.
|
||||||
|
pub trait VersionSet: Debug + Display + Clone + Eq {
|
||||||
|
/// Version type associated with the sets manipulated.
|
||||||
|
type V: Debug + Display + Clone + Ord;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
/// Constructor for an empty set containing no version.
|
||||||
|
fn empty() -> Self;
|
||||||
|
/// Constructor for a set containing exactly one version.
|
||||||
|
fn singleton(v: Self::V) -> Self;
|
||||||
|
|
||||||
|
// Operations
|
||||||
|
/// Compute the complement of this set.
|
||||||
|
fn complement(&self) -> Self;
|
||||||
|
/// Compute the intersection with another set.
|
||||||
|
fn intersection(&self, other: &Self) -> Self;
|
||||||
|
|
||||||
|
// Membership
|
||||||
|
/// Evaluate membership of a version in this set.
|
||||||
|
fn contains(&self, v: &Self::V) -> bool;
|
||||||
|
|
||||||
|
// Automatically implemented functions ###########################
|
||||||
|
|
||||||
|
/// Constructor for the set containing all versions.
|
||||||
|
/// Automatically implemented as `Self::empty().complement()`.
|
||||||
|
fn full() -> Self {
|
||||||
|
Self::empty().complement()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the union with another set.
|
||||||
|
/// Thanks to set properties, this is automatically implemented as:
|
||||||
|
/// `self.complement().intersection(&other.complement()).complement()`
|
||||||
|
fn union(&self, other: &Self) -> Self {
|
||||||
|
self.complement()
|
||||||
|
.intersection(&other.complement())
|
||||||
|
.complement()
|
||||||
|
}
|
||||||
|
}
|
87
vendor/pubgrub/tests/examples.rs
vendored
87
vendor/pubgrub/tests/examples.rs
vendored
|
@ -5,22 +5,37 @@ use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::type_aliases::Map;
|
use pubgrub::type_aliases::Map;
|
||||||
use pubgrub::version::{NumberVersion, SemanticVersion};
|
use pubgrub::version::{NumberVersion, SemanticVersion};
|
||||||
|
|
||||||
|
type NumVS = Range<NumberVersion>;
|
||||||
|
type SemVS = Range<SemanticVersion>;
|
||||||
|
|
||||||
|
use log::LevelFilter;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
fn init_log() {
|
||||||
|
let _ = env_logger::builder()
|
||||||
|
.filter_level(LevelFilter::Trace)
|
||||||
|
.format(|buf, record| writeln!(buf, "{}", record.args()))
|
||||||
|
.is_test(true)
|
||||||
|
.try_init();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#no-conflicts
|
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#no-conflicts
|
||||||
fn no_conflict() {
|
fn no_conflict() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
init_log();
|
||||||
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", (1, 0, 0),
|
"root", (1, 0, 0),
|
||||||
vec![("foo", Range::between((1, 0, 0), (2, 0, 0)))],
|
[("foo", Range::between((1, 0, 0), (2, 0, 0)))],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (1, 0, 0),
|
"foo", (1, 0, 0),
|
||||||
vec![("bar", Range::between((1, 0, 0), (2, 0, 0)))],
|
[("bar", Range::between((1, 0, 0), (2, 0, 0)))],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("bar", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("bar", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("bar", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("bar", (2, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
let computed_solution = resolve(&dependency_provider, "root", (1, 0, 0)).unwrap();
|
let computed_solution = resolve(&dependency_provider, "root", (1, 0, 0)).unwrap();
|
||||||
|
@ -38,11 +53,12 @@ fn no_conflict() {
|
||||||
#[test]
|
#[test]
|
||||||
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#avoiding-conflict-during-decision-making
|
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#avoiding-conflict-during-decision-making
|
||||||
fn avoiding_conflict_during_decision_making() {
|
fn avoiding_conflict_during_decision_making() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
init_log();
|
||||||
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", (1, 0, 0),
|
"root", (1, 0, 0),
|
||||||
vec![
|
[
|
||||||
("foo", Range::between((1, 0, 0), (2, 0, 0))),
|
("foo", Range::between((1, 0, 0), (2, 0, 0))),
|
||||||
("bar", Range::between((1, 0, 0), (2, 0, 0))),
|
("bar", Range::between((1, 0, 0), (2, 0, 0))),
|
||||||
],
|
],
|
||||||
|
@ -50,12 +66,12 @@ fn avoiding_conflict_during_decision_making() {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (1, 1, 0),
|
"foo", (1, 1, 0),
|
||||||
vec![("bar", Range::between((2, 0, 0), (3, 0, 0)))],
|
[("bar", Range::between((2, 0, 0), (3, 0, 0)))],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("foo", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("foo", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("bar", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("bar", (1, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("bar", (1, 1, 0), vec![]);
|
dependency_provider.add_dependencies("bar", (1, 1, 0), []);
|
||||||
dependency_provider.add_dependencies("bar", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("bar", (2, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
let computed_solution = resolve(&dependency_provider, "root", (1, 0, 0)).unwrap();
|
let computed_solution = resolve(&dependency_provider, "root", (1, 0, 0)).unwrap();
|
||||||
|
@ -73,22 +89,23 @@ fn avoiding_conflict_during_decision_making() {
|
||||||
#[test]
|
#[test]
|
||||||
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#performing-conflict-resolution
|
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#performing-conflict-resolution
|
||||||
fn conflict_resolution() {
|
fn conflict_resolution() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
init_log();
|
||||||
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", (1, 0, 0),
|
"root", (1, 0, 0),
|
||||||
vec![("foo", Range::higher_than((1, 0, 0)))],
|
[("foo", Range::higher_than((1, 0, 0)))],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (2, 0, 0),
|
"foo", (2, 0, 0),
|
||||||
vec![("bar", Range::between((1, 0, 0), (2, 0, 0)))],
|
[("bar", Range::between((1, 0, 0), (2, 0, 0)))],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("foo", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("foo", (1, 0, 0), []);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"bar", (1, 0, 0),
|
"bar", (1, 0, 0),
|
||||||
vec![("foo", Range::between((1, 0, 0), (2, 0, 0)))],
|
[("foo", Range::between((1, 0, 0), (2, 0, 0)))],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
|
@ -106,12 +123,13 @@ fn conflict_resolution() {
|
||||||
#[test]
|
#[test]
|
||||||
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#conflict-resolution-with-a-partial-satisfier
|
/// https://github.com/dart-lang/pub/blob/master/doc/solver.md#conflict-resolution-with-a-partial-satisfier
|
||||||
fn conflict_with_partial_satisfier() {
|
fn conflict_with_partial_satisfier() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, SemanticVersion>::new();
|
init_log();
|
||||||
|
let mut dependency_provider = OfflineDependencyProvider::<&str, SemVS>::new();
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// root 1.0.0 depends on foo ^1.0.0 and target ^2.0.0
|
// root 1.0.0 depends on foo ^1.0.0 and target ^2.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"root", (1, 0, 0),
|
"root", (1, 0, 0),
|
||||||
vec![
|
[
|
||||||
("foo", Range::between((1, 0, 0), (2, 0, 0))),
|
("foo", Range::between((1, 0, 0), (2, 0, 0))),
|
||||||
("target", Range::between((2, 0, 0), (3, 0, 0))),
|
("target", Range::between((2, 0, 0), (3, 0, 0))),
|
||||||
],
|
],
|
||||||
|
@ -120,33 +138,33 @@ fn conflict_with_partial_satisfier() {
|
||||||
// foo 1.1.0 depends on left ^1.0.0 and right ^1.0.0
|
// foo 1.1.0 depends on left ^1.0.0 and right ^1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"foo", (1, 1, 0),
|
"foo", (1, 1, 0),
|
||||||
vec![
|
[
|
||||||
("left", Range::between((1, 0, 0), (2, 0, 0))),
|
("left", Range::between((1, 0, 0), (2, 0, 0))),
|
||||||
("right", Range::between((1, 0, 0), (2, 0, 0))),
|
("right", Range::between((1, 0, 0), (2, 0, 0))),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("foo", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("foo", (1, 0, 0), []);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// left 1.0.0 depends on shared >=1.0.0
|
// left 1.0.0 depends on shared >=1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"left", (1, 0, 0),
|
"left", (1, 0, 0),
|
||||||
vec![("shared", Range::higher_than((1, 0, 0)))],
|
[("shared", Range::higher_than((1, 0, 0)))],
|
||||||
);
|
);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// right 1.0.0 depends on shared <2.0.0
|
// right 1.0.0 depends on shared <2.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"right", (1, 0, 0),
|
"right", (1, 0, 0),
|
||||||
vec![("shared", Range::strictly_lower_than((2, 0, 0)))],
|
[("shared", Range::strictly_lower_than((2, 0, 0)))],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("shared", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("shared", (2, 0, 0), []);
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
// shared 1.0.0 depends on target ^1.0.0
|
// shared 1.0.0 depends on target ^1.0.0
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
"shared", (1, 0, 0),
|
"shared", (1, 0, 0),
|
||||||
vec![("target", Range::between((1, 0, 0), (2, 0, 0)))],
|
[("target", Range::between((1, 0, 0), (2, 0, 0)))],
|
||||||
);
|
);
|
||||||
dependency_provider.add_dependencies("target", (2, 0, 0), vec![]);
|
dependency_provider.add_dependencies("target", (2, 0, 0), []);
|
||||||
dependency_provider.add_dependencies("target", (1, 0, 0), vec![]);
|
dependency_provider.add_dependencies("target", (1, 0, 0), []);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
let computed_solution = resolve(&dependency_provider, "root", (1, 0, 0)).unwrap();
|
let computed_solution = resolve(&dependency_provider, "root", (1, 0, 0)).unwrap();
|
||||||
|
@ -171,13 +189,14 @@ fn conflict_with_partial_satisfier() {
|
||||||
///
|
///
|
||||||
/// Solution: a0, b0, c0, d0
|
/// Solution: a0, b0, c0, d0
|
||||||
fn double_choices() {
|
fn double_choices() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<&str, NumberVersion>::new();
|
init_log();
|
||||||
dependency_provider.add_dependencies("a", 0, vec![("b", Range::any()), ("c", Range::any())]);
|
let mut dependency_provider = OfflineDependencyProvider::<&str, NumVS>::new();
|
||||||
dependency_provider.add_dependencies("b", 0, vec![("d", Range::exact(0))]);
|
dependency_provider.add_dependencies("a", 0, [("b", Range::full()), ("c", Range::full())]);
|
||||||
dependency_provider.add_dependencies("b", 1, vec![("d", Range::exact(1))]);
|
dependency_provider.add_dependencies("b", 0, [("d", Range::singleton(0))]);
|
||||||
dependency_provider.add_dependencies("c", 0, vec![]);
|
dependency_provider.add_dependencies("b", 1, [("d", Range::singleton(1))]);
|
||||||
dependency_provider.add_dependencies("c", 1, vec![("d", Range::exact(2))]);
|
dependency_provider.add_dependencies("c", 0, []);
|
||||||
dependency_provider.add_dependencies("d", 0, vec![]);
|
dependency_provider.add_dependencies("c", 1, [("d", Range::singleton(2))]);
|
||||||
|
dependency_provider.add_dependencies("d", 0, []);
|
||||||
|
|
||||||
// Solution.
|
// Solution.
|
||||||
let mut expected_solution = Map::default();
|
let mut expected_solution = Map::default();
|
||||||
|
|
103
vendor/pubgrub/tests/proptest.rs
vendored
103
vendor/pubgrub/tests/proptest.rs
vendored
|
@ -10,7 +10,8 @@ use pubgrub::solver::{
|
||||||
choose_package_with_fewest_versions, resolve, Dependencies, DependencyProvider,
|
choose_package_with_fewest_versions, resolve, Dependencies, DependencyProvider,
|
||||||
OfflineDependencyProvider,
|
OfflineDependencyProvider,
|
||||||
};
|
};
|
||||||
use pubgrub::version::{NumberVersion, Version};
|
use pubgrub::version::{NumberVersion, SemanticVersion};
|
||||||
|
use pubgrub::version_set::VersionSet;
|
||||||
|
|
||||||
use proptest::collection::{btree_map, vec};
|
use proptest::collection::{btree_map, vec};
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
@ -24,20 +25,28 @@ mod sat_dependency_provider;
|
||||||
/// The same as [OfflineDependencyProvider] but takes versions from the opposite end:
|
/// 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.
|
/// if [OfflineDependencyProvider] returns versions from newest to oldest, this returns them from oldest to newest.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct OldestVersionsDependencyProvider<P: Package, V: Version>(OfflineDependencyProvider<P, V>);
|
struct OldestVersionsDependencyProvider<P: Package, VS: VersionSet>(
|
||||||
|
OfflineDependencyProvider<P, VS>,
|
||||||
|
);
|
||||||
|
|
||||||
impl<P: Package, V: Version> DependencyProvider<P, V> for OldestVersionsDependencyProvider<P, V> {
|
impl<P: Package, VS: VersionSet> DependencyProvider<P, VS>
|
||||||
fn choose_package_version<T: std::borrow::Borrow<P>, U: std::borrow::Borrow<Range<V>>>(
|
for OldestVersionsDependencyProvider<P, VS>
|
||||||
|
{
|
||||||
|
fn choose_package_version<T: std::borrow::Borrow<P>, U: std::borrow::Borrow<VS>>(
|
||||||
&self,
|
&self,
|
||||||
potential_packages: impl Iterator<Item = (T, U)>,
|
potential_packages: impl Iterator<Item = (T, U)>,
|
||||||
) -> Result<(T, Option<V>), Box<dyn Error>> {
|
) -> Result<(T, Option<VS::V>), Box<dyn Error + Send + Sync>> {
|
||||||
Ok(choose_package_with_fewest_versions(
|
Ok(choose_package_with_fewest_versions(
|
||||||
|p| self.0.versions(p).into_iter().flatten().cloned(),
|
|p| self.0.versions(p).into_iter().flatten().cloned(),
|
||||||
potential_packages,
|
potential_packages,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dependencies(&self, p: &P, v: &V) -> Result<Dependencies<P, V>, Box<dyn Error>> {
|
fn get_dependencies(
|
||||||
|
&self,
|
||||||
|
p: &P,
|
||||||
|
v: &VS::V,
|
||||||
|
) -> Result<Dependencies<P, VS>, Box<dyn Error + Send + Sync>> {
|
||||||
self.0.get_dependencies(p, v)
|
self.0.get_dependencies(p, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,21 +71,25 @@ impl<DP> TimeoutDependencyProvider<DP> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P, V>
|
impl<P: Package, VS: VersionSet, DP: DependencyProvider<P, VS>> DependencyProvider<P, VS>
|
||||||
for TimeoutDependencyProvider<DP>
|
for TimeoutDependencyProvider<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,
|
&self,
|
||||||
potential_packages: impl Iterator<Item = (T, U)>,
|
potential_packages: impl Iterator<Item = (T, U)>,
|
||||||
) -> Result<(T, Option<V>), Box<dyn Error>> {
|
) -> Result<(T, Option<VS::V>), Box<dyn Error + Send + Sync>> {
|
||||||
self.dp.choose_package_version(potential_packages)
|
self.dp.choose_package_version(potential_packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dependencies(&self, p: &P, v: &V) -> Result<Dependencies<P, V>, Box<dyn Error>> {
|
fn get_dependencies(
|
||||||
|
&self,
|
||||||
|
p: &P,
|
||||||
|
v: &VS::V,
|
||||||
|
) -> Result<Dependencies<P, VS>, Box<dyn Error + Send + Sync>> {
|
||||||
self.dp.get_dependencies(p, v)
|
self.dp.get_dependencies(p, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_cancel(&self) -> Result<(), Box<dyn Error>> {
|
fn should_cancel(&self) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
assert!(self.start_time.elapsed().as_secs() < 60);
|
assert!(self.start_time.elapsed().as_secs() < 60);
|
||||||
let calls = self.call_count.get();
|
let calls = self.call_count.get();
|
||||||
assert!(calls < self.max_calls);
|
assert!(calls < self.max_calls);
|
||||||
|
@ -85,11 +98,14 @@ impl<P: Package, V: Version, DP: DependencyProvider<P, V>> DependencyProvider<P,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NumVS = Range<NumberVersion>;
|
||||||
|
type SemVS = Range<SemanticVersion>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn should_cancel_can_panic() {
|
fn should_cancel_can_panic() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<_, NumberVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<_, NumVS>::new();
|
||||||
dependency_provider.add_dependencies(0, 0, vec![(666, Range::any())]);
|
dependency_provider.add_dependencies(0, 0, [(666, Range::full())]);
|
||||||
|
|
||||||
// Run the algorithm.
|
// Run the algorithm.
|
||||||
let _ = resolve(
|
let _ = resolve(
|
||||||
|
@ -116,12 +132,7 @@ fn string_names() -> impl Strategy<Value = String> {
|
||||||
pub fn registry_strategy<N: Package + Ord>(
|
pub fn registry_strategy<N: Package + Ord>(
|
||||||
name: impl Strategy<Value = N>,
|
name: impl Strategy<Value = N>,
|
||||||
bad_name: N,
|
bad_name: N,
|
||||||
) -> impl Strategy<
|
) -> impl Strategy<Value = (OfflineDependencyProvider<N, NumVS>, Vec<(N, NumberVersion)>)> {
|
||||||
Value = (
|
|
||||||
OfflineDependencyProvider<N, NumberVersion>,
|
|
||||||
Vec<(N, NumberVersion)>,
|
|
||||||
),
|
|
||||||
> {
|
|
||||||
let max_crates = 40;
|
let max_crates = 40;
|
||||||
let max_versions = 15;
|
let max_versions = 15;
|
||||||
let shrinkage = 40;
|
let shrinkage = 40;
|
||||||
|
@ -166,20 +177,18 @@ pub fn registry_strategy<N: Package + Ord>(
|
||||||
)
|
)
|
||||||
.prop_map(
|
.prop_map(
|
||||||
move |(crate_vers_by_name, raw_dependencies, reverse_alphabetical, complicated_len)| {
|
move |(crate_vers_by_name, raw_dependencies, reverse_alphabetical, complicated_len)| {
|
||||||
let mut list_of_pkgid: Vec<(
|
let mut list_of_pkgid: Vec<((N, NumberVersion), Option<Vec<(N, NumVS)>>)> =
|
||||||
(N, NumberVersion),
|
crate_vers_by_name
|
||||||
Option<Vec<(N, Range<NumberVersion>)>>,
|
.iter()
|
||||||
)> = crate_vers_by_name
|
.flat_map(|(name, vers)| {
|
||||||
.iter()
|
vers.iter().map(move |x| {
|
||||||
.flat_map(|(name, vers)| {
|
(
|
||||||
vers.iter().map(move |x| {
|
(name.clone(), NumberVersion::from(x.0)),
|
||||||
(
|
if x.1 { Some(vec![]) } else { None },
|
||||||
(name.clone(), NumberVersion::from(x.0)),
|
)
|
||||||
if x.1 { Some(vec![]) } else { None },
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
.collect();
|
||||||
.collect();
|
|
||||||
let len_all_pkgid = list_of_pkgid.len();
|
let len_all_pkgid = list_of_pkgid.len();
|
||||||
for (a, b, (c, d)) in raw_dependencies {
|
for (a, b, (c, d)) in raw_dependencies {
|
||||||
let (a, b) = order_index(a, b, len_all_pkgid);
|
let (a, b) = order_index(a, b, len_all_pkgid);
|
||||||
|
@ -196,13 +205,13 @@ pub fn registry_strategy<N: Package + Ord>(
|
||||||
deps.push((
|
deps.push((
|
||||||
dep_name,
|
dep_name,
|
||||||
if c == 0 && d == s_last_index {
|
if c == 0 && d == s_last_index {
|
||||||
Range::any()
|
Range::full()
|
||||||
} else if c == 0 {
|
} else if c == 0 {
|
||||||
Range::strictly_lower_than(s[d].0 + 1)
|
Range::strictly_lower_than(s[d].0 + 1)
|
||||||
} else if d == s_last_index {
|
} else if d == s_last_index {
|
||||||
Range::higher_than(s[c].0)
|
Range::higher_than(s[c].0)
|
||||||
} else if c == d {
|
} else if c == d {
|
||||||
Range::exact(s[c].0)
|
Range::singleton(s[c].0)
|
||||||
} else {
|
} else {
|
||||||
Range::between(s[c].0, s[d].0 + 1)
|
Range::between(s[c].0, s[d].0 + 1)
|
||||||
},
|
},
|
||||||
|
@ -210,7 +219,7 @@ pub fn registry_strategy<N: Package + Ord>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<N, NumberVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<N, NumVS>::new();
|
||||||
|
|
||||||
let complicated_len = std::cmp::min(complicated_len, list_of_pkgid.len());
|
let complicated_len = std::cmp::min(complicated_len, list_of_pkgid.len());
|
||||||
let complicated: Vec<_> = if reverse_alphabetical {
|
let complicated: Vec<_> = if reverse_alphabetical {
|
||||||
|
@ -226,7 +235,7 @@ pub fn registry_strategy<N: Package + Ord>(
|
||||||
dependency_provider.add_dependencies(
|
dependency_provider.add_dependencies(
|
||||||
name,
|
name,
|
||||||
ver,
|
ver,
|
||||||
deps.unwrap_or_else(|| vec![(bad_name.clone(), Range::any())]),
|
deps.unwrap_or_else(|| vec![(bad_name.clone(), Range::full())]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,8 +342,8 @@ proptest! {
|
||||||
(Ok(l), Ok(r)) => assert_eq!(l, r),
|
(Ok(l), Ok(r)) => assert_eq!(l, r),
|
||||||
(Err(PubGrubError::NoSolution(derivation_l)), Err(PubGrubError::NoSolution(derivation_r))) => {
|
(Err(PubGrubError::NoSolution(derivation_l)), Err(PubGrubError::NoSolution(derivation_r))) => {
|
||||||
prop_assert_eq!(
|
prop_assert_eq!(
|
||||||
DefaultStringReporter::report(&derivation_l),
|
DefaultStringReporter::report(derivation_l),
|
||||||
DefaultStringReporter::report(&derivation_r)
|
DefaultStringReporter::report(derivation_r)
|
||||||
)},
|
)},
|
||||||
_ => panic!("not the same result")
|
_ => panic!("not the same result")
|
||||||
}
|
}
|
||||||
|
@ -373,7 +382,7 @@ proptest! {
|
||||||
.versions(package)
|
.versions(package)
|
||||||
.unwrap().collect();
|
.unwrap().collect();
|
||||||
let version = version_idx.get(&versions);
|
let version = version_idx.get(&versions);
|
||||||
let dependencies: Vec<(u16, Range<NumberVersion>)> = match dependency_provider
|
let dependencies: Vec<(u16, NumVS)> = match dependency_provider
|
||||||
.get_dependencies(package, version)
|
.get_dependencies(package, version)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
{
|
{
|
||||||
|
@ -423,7 +432,7 @@ proptest! {
|
||||||
dependency_provider
|
dependency_provider
|
||||||
.versions(&p)
|
.versions(&p)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(move |v| (p, v.clone()))
|
.map(move |&v| (p, v))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let to_remove: Set<(_, _)> = indexes_to_remove.iter().map(|x| x.get(&all_versions)).cloned().collect();
|
let to_remove: Set<(_, _)> = indexes_to_remove.iter().map(|x| x.get(&all_versions)).cloned().collect();
|
||||||
|
@ -432,7 +441,7 @@ proptest! {
|
||||||
Ok(used) => {
|
Ok(used) => {
|
||||||
// If resolution was successful, then unpublishing a version of a crate
|
// If resolution was successful, then unpublishing a version of a crate
|
||||||
// that was not selected should not change that.
|
// that was not selected should not change that.
|
||||||
let mut smaller_dependency_provider = OfflineDependencyProvider::<_, NumberVersion>::new();
|
let mut smaller_dependency_provider = OfflineDependencyProvider::<_, NumVS>::new();
|
||||||
for &(n, v) in &all_versions {
|
for &(n, v) in &all_versions {
|
||||||
if used.get(&n) == Some(&v) // it was used
|
if used.get(&n) == Some(&v) // it was used
|
||||||
|| to_remove.get(&(n, v)).is_none() // or it is not one to be removed
|
|| to_remove.get(&(n, v)).is_none() // or it is not one to be removed
|
||||||
|
@ -455,7 +464,7 @@ proptest! {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// If resolution was unsuccessful, then it should stay unsuccessful
|
// If resolution was unsuccessful, then it should stay unsuccessful
|
||||||
// even if any version of a crate is unpublished.
|
// even if any version of a crate is unpublished.
|
||||||
let mut smaller_dependency_provider = OfflineDependencyProvider::<_, NumberVersion>::new();
|
let mut smaller_dependency_provider = OfflineDependencyProvider::<_, NumVS>::new();
|
||||||
for &(n, v) in &all_versions {
|
for &(n, v) in &all_versions {
|
||||||
if to_remove.get(&(n, v)).is_none() // it is not one to be removed
|
if to_remove.get(&(n, v)).is_none() // it is not one to be removed
|
||||||
{
|
{
|
||||||
|
@ -488,7 +497,7 @@ fn large_case() {
|
||||||
eprintln!("{}", name);
|
eprintln!("{}", name);
|
||||||
let data = std::fs::read_to_string(&case).unwrap();
|
let data = std::fs::read_to_string(&case).unwrap();
|
||||||
if name.ends_with("u16_NumberVersion.ron") {
|
if name.ends_with("u16_NumberVersion.ron") {
|
||||||
let dependency_provider: OfflineDependencyProvider<u16, NumberVersion> =
|
let dependency_provider: OfflineDependencyProvider<u16, NumVS> =
|
||||||
ron::de::from_str(&data).unwrap();
|
ron::de::from_str(&data).unwrap();
|
||||||
let mut sat = SatResolve::new(&dependency_provider);
|
let mut sat = SatResolve::new(&dependency_provider);
|
||||||
for p in dependency_provider.packages() {
|
for p in dependency_provider.packages() {
|
||||||
|
@ -501,14 +510,12 @@ fn large_case() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if name.ends_with("str_SemanticVersion.ron") {
|
} else if name.ends_with("str_SemanticVersion.ron") {
|
||||||
let dependency_provider: OfflineDependencyProvider<
|
let dependency_provider: OfflineDependencyProvider<&str, SemVS> =
|
||||||
&str,
|
ron::de::from_str(&data).unwrap();
|
||||||
pubgrub::version::SemanticVersion,
|
|
||||||
> = ron::de::from_str(&data).unwrap();
|
|
||||||
let mut sat = SatResolve::new(&dependency_provider);
|
let mut sat = SatResolve::new(&dependency_provider);
|
||||||
for p in dependency_provider.packages() {
|
for p in dependency_provider.packages() {
|
||||||
for n in dependency_provider.versions(p).unwrap() {
|
for n in dependency_provider.versions(p).unwrap() {
|
||||||
if let Ok(s) = resolve(&dependency_provider, p.clone(), n.clone()) {
|
if let Ok(s) = resolve(&dependency_provider, p, n.clone()) {
|
||||||
assert!(sat.sat_is_valid_solution(&s));
|
assert!(sat.sat_is_valid_solution(&s));
|
||||||
} else {
|
} else {
|
||||||
assert!(!sat.sat_resolve(p, &n));
|
assert!(!sat.sat_resolve(p, &n));
|
||||||
|
|
18
vendor/pubgrub/tests/sat_dependency_provider.rs
vendored
18
vendor/pubgrub/tests/sat_dependency_provider.rs
vendored
|
@ -3,7 +3,7 @@
|
||||||
use pubgrub::package::Package;
|
use pubgrub::package::Package;
|
||||||
use pubgrub::solver::{Dependencies, DependencyProvider, OfflineDependencyProvider};
|
use pubgrub::solver::{Dependencies, DependencyProvider, OfflineDependencyProvider};
|
||||||
use pubgrub::type_aliases::{Map, SelectedDependencies};
|
use pubgrub::type_aliases::{Map, SelectedDependencies};
|
||||||
use pubgrub::version::Version;
|
use pubgrub::version_set::VersionSet;
|
||||||
use varisat::ExtendFormula;
|
use varisat::ExtendFormula;
|
||||||
|
|
||||||
const fn num_bits<T>() -> usize {
|
const fn num_bits<T>() -> usize {
|
||||||
|
@ -46,17 +46,17 @@ fn sat_at_most_one(solver: &mut impl varisat::ExtendFormula, vars: &[varisat::Va
|
||||||
///
|
///
|
||||||
/// The SAT library does not optimize for the newer version,
|
/// The SAT library does not optimize for the newer version,
|
||||||
/// so the selected packages may not match the real resolver.
|
/// so the selected packages may not match the real resolver.
|
||||||
pub struct SatResolve<P: Package, V: Version> {
|
pub struct SatResolve<P: Package, VS: VersionSet> {
|
||||||
solver: varisat::Solver<'static>,
|
solver: varisat::Solver<'static>,
|
||||||
all_versions_by_p: Map<P, Vec<(V, varisat::Var)>>,
|
all_versions_by_p: Map<P, Vec<(VS::V, varisat::Var)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Package, V: Version> SatResolve<P, V> {
|
impl<P: Package, VS: VersionSet> SatResolve<P, VS> {
|
||||||
pub fn new(dp: &OfflineDependencyProvider<P, V>) -> Self {
|
pub fn new(dp: &OfflineDependencyProvider<P, VS>) -> Self {
|
||||||
let mut cnf = varisat::CnfFormula::new();
|
let mut cnf = varisat::CnfFormula::new();
|
||||||
|
|
||||||
let mut all_versions = vec![];
|
let mut all_versions = vec![];
|
||||||
let mut all_versions_by_p: Map<P, Vec<(V, varisat::Var)>> = Map::default();
|
let mut all_versions_by_p: Map<P, Vec<(VS::V, varisat::Var)>> = Map::default();
|
||||||
|
|
||||||
for p in dp.packages() {
|
for p in dp.packages() {
|
||||||
let mut versions_for_p = vec![];
|
let mut versions_for_p = vec![];
|
||||||
|
@ -82,7 +82,7 @@ impl<P: Package, V: Version> SatResolve<P, V> {
|
||||||
for (p1, range) in &deps {
|
for (p1, range) in &deps {
|
||||||
let empty_vec = vec![];
|
let empty_vec = vec![];
|
||||||
let mut matches: Vec<varisat::Lit> = all_versions_by_p
|
let mut matches: Vec<varisat::Lit> = all_versions_by_p
|
||||||
.get(&p1)
|
.get(p1)
|
||||||
.unwrap_or(&empty_vec)
|
.unwrap_or(&empty_vec)
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(v1, _)| range.contains(v1))
|
.filter(|(v1, _)| range.contains(v1))
|
||||||
|
@ -110,7 +110,7 @@ impl<P: Package, V: Version> SatResolve<P, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sat_resolve(&mut self, name: &P, ver: &V) -> bool {
|
pub fn sat_resolve(&mut self, name: &P, ver: &VS::V) -> bool {
|
||||||
if let Some(vers) = self.all_versions_by_p.get(name) {
|
if let Some(vers) = self.all_versions_by_p.get(name) {
|
||||||
if let Some((_, var)) = vers.iter().find(|(v, _)| v == ver) {
|
if let Some((_, var)) = vers.iter().find(|(v, _)| v == ver) {
|
||||||
self.solver.assume(&[var.positive()]);
|
self.solver.assume(&[var.positive()]);
|
||||||
|
@ -126,7 +126,7 @@ impl<P: Package, V: Version> SatResolve<P, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sat_is_valid_solution(&mut self, pids: &SelectedDependencies<P, V>) -> bool {
|
pub fn sat_is_valid_solution(&mut self, pids: &SelectedDependencies<P, VS::V>) -> bool {
|
||||||
let mut assumption = vec![];
|
let mut assumption = vec![];
|
||||||
|
|
||||||
for (p, vs) in &self.all_versions_by_p {
|
for (p, vs) in &self.all_versions_by_p {
|
||||||
|
|
24
vendor/pubgrub/tests/tests.rs
vendored
24
vendor/pubgrub/tests/tests.rs
vendored
|
@ -5,16 +5,18 @@ use pubgrub::range::Range;
|
||||||
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
use pubgrub::solver::{resolve, OfflineDependencyProvider};
|
||||||
use pubgrub::version::NumberVersion;
|
use pubgrub::version::NumberVersion;
|
||||||
|
|
||||||
|
type NumVS = Range<NumberVersion>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn same_result_on_repeated_runs() {
|
fn same_result_on_repeated_runs() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<_, NumberVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<_, NumVS>::new();
|
||||||
|
|
||||||
dependency_provider.add_dependencies("c", 0, vec![]);
|
dependency_provider.add_dependencies("c", 0, []);
|
||||||
dependency_provider.add_dependencies("c", 2, vec![]);
|
dependency_provider.add_dependencies("c", 2, []);
|
||||||
dependency_provider.add_dependencies("b", 0, vec![]);
|
dependency_provider.add_dependencies("b", 0, []);
|
||||||
dependency_provider.add_dependencies("b", 1, vec![("c", Range::between(0, 1))]);
|
dependency_provider.add_dependencies("b", 1, [("c", Range::between(0, 1))]);
|
||||||
|
|
||||||
dependency_provider.add_dependencies("a", 0, vec![("b", Range::any()), ("c", Range::any())]);
|
dependency_provider.add_dependencies("a", 0, [("b", Range::full()), ("c", Range::full())]);
|
||||||
|
|
||||||
let name = "a";
|
let name = "a";
|
||||||
let ver = NumberVersion(0);
|
let ver = NumberVersion(0);
|
||||||
|
@ -29,14 +31,14 @@ fn same_result_on_repeated_runs() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_always_find_a_satisfier() {
|
fn should_always_find_a_satisfier() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<_, NumberVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<_, NumVS>::new();
|
||||||
dependency_provider.add_dependencies("a", 0, vec![("b", Range::none())]);
|
dependency_provider.add_dependencies("a", 0, [("b", Range::empty())]);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
resolve(&dependency_provider, "a", 0),
|
resolve(&dependency_provider, "a", 0),
|
||||||
Err(PubGrubError::DependencyOnTheEmptySet { .. })
|
Err(PubGrubError::DependencyOnTheEmptySet { .. })
|
||||||
));
|
));
|
||||||
|
|
||||||
dependency_provider.add_dependencies("c", 0, vec![("a", Range::any())]);
|
dependency_provider.add_dependencies("c", 0, [("a", Range::full())]);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
resolve(&dependency_provider, "c", 0),
|
resolve(&dependency_provider, "c", 0),
|
||||||
Err(PubGrubError::DependencyOnTheEmptySet { .. })
|
Err(PubGrubError::DependencyOnTheEmptySet { .. })
|
||||||
|
@ -45,8 +47,8 @@ fn should_always_find_a_satisfier() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cannot_depend_on_self() {
|
fn cannot_depend_on_self() {
|
||||||
let mut dependency_provider = OfflineDependencyProvider::<_, NumberVersion>::new();
|
let mut dependency_provider = OfflineDependencyProvider::<_, NumVS>::new();
|
||||||
dependency_provider.add_dependencies("a", 0, vec![("a", Range::any())]);
|
dependency_provider.add_dependencies("a", 0, [("a", Range::full())]);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
resolve(&dependency_provider, "a", 0),
|
resolve(&dependency_provider, "a", 0),
|
||||||
Err(PubGrubError::SelfDependency { .. })
|
Err(PubGrubError::SelfDependency { .. })
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue