mirror of https://github.com/astral-sh/uv
Start using the version ranges crate (#8667)
This commit is contained in:
parent
a56e521179
commit
36102dbd0e
|
|
@ -2498,13 +2498,14 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pubgrub"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/astral-sh/pubgrub?rev=7243f4faf8e54837aa8a401a18406e7173de4ad5#7243f4faf8e54837aa8a401a18406e7173de4ad5"
|
||||
source = "git+https://github.com/astral-sh/pubgrub?rev=95e1390399cdddee986b658be19587eb1fdb2d79#95e1390399cdddee986b658be19587eb1fdb2d79"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"log",
|
||||
"priority-queue",
|
||||
"rustc-hash",
|
||||
"thiserror",
|
||||
"version-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4885,7 +4886,6 @@ dependencies = [
|
|||
"insta",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"pubgrub",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"schemars",
|
||||
|
|
@ -4901,6 +4901,7 @@ dependencies = [
|
|||
"uv-normalize",
|
||||
"uv-pep440",
|
||||
"uv-pubgrub",
|
||||
"version-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4933,9 +4934,9 @@ name = "uv-pubgrub"
|
|||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"pubgrub",
|
||||
"thiserror",
|
||||
"uv-pep440",
|
||||
"version-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5376,6 +5377,14 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "version-ranges"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/astral-sh/pubgrub?rev=95e1390399cdddee986b658be19587eb1fdb2d79#95e1390399cdddee986b658be19587eb1fdb2d79"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ pathdiff = { version = "0.2.1" }
|
|||
petgraph = { version = "0.6.5" }
|
||||
platform-info = { version = "2.0.3" }
|
||||
proc-macro2 = { version = "1.0.86" }
|
||||
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "7243f4faf8e54837aa8a401a18406e7173de4ad5" }
|
||||
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "95e1390399cdddee986b658be19587eb1fdb2d79" }
|
||||
version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "95e1390399cdddee986b658be19587eb1fdb2d79" }
|
||||
quote = { version = "1.0.37" }
|
||||
rayon = { version = "1.10.0" }
|
||||
reflink-copy = { version = "0.1.19" }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ uv-pubgrub = { workspace = true }
|
|||
boxcar = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
pubgrub = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
|
|
@ -40,6 +39,7 @@ thiserror = { workspace = true }
|
|||
tracing = { workspace = true, optional = true }
|
||||
unicode-width = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
version-ranges = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.40.0" }
|
||||
|
|
|
|||
|
|
@ -51,12 +51,12 @@ use std::sync::Mutex;
|
|||
use std::sync::MutexGuard;
|
||||
|
||||
use itertools::Either;
|
||||
use pubgrub::Range;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::sync::LazyLock;
|
||||
use uv_pep440::Operator;
|
||||
use uv_pep440::{Version, VersionSpecifier};
|
||||
use uv_pubgrub::PubGrubSpecifier;
|
||||
use version_ranges::Ranges;
|
||||
|
||||
use crate::marker::MarkerValueExtra;
|
||||
use crate::ExtraOperator;
|
||||
|
|
@ -403,7 +403,7 @@ impl InternerGuard<'_> {
|
|||
});
|
||||
return self.create_node(node.var.clone(), children);
|
||||
};
|
||||
let py_range = Range::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
||||
let py_range = Ranges::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
||||
if py_range.is_empty() {
|
||||
// Oops, the bounds imply there is nothing that can match,
|
||||
// so we always evaluate to false.
|
||||
|
|
@ -428,12 +428,12 @@ impl InternerGuard<'_> {
|
|||
// are known to be satisfied.
|
||||
let &(ref first_range, first_node_id) = new.first().unwrap();
|
||||
let first_upper = first_range.bounding_range().unwrap().1;
|
||||
let clipped = Range::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
||||
let clipped = Ranges::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
||||
*new.first_mut().unwrap() = (clipped, first_node_id);
|
||||
|
||||
let &(ref last_range, last_node_id) = new.last().unwrap();
|
||||
let last_lower = last_range.bounding_range().unwrap().0;
|
||||
let clipped = Range::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
||||
let clipped = Ranges::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
||||
*new.last_mut().unwrap() = (clipped, last_node_id);
|
||||
|
||||
self.create_node(node.var.clone(), Edges::Version { edges: new })
|
||||
|
|
@ -459,7 +459,7 @@ impl InternerGuard<'_> {
|
|||
return i;
|
||||
}
|
||||
|
||||
let py_range = Range::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
||||
let py_range = Ranges::from_range_bounds((py_lower.cloned(), py_upper.cloned()));
|
||||
if py_range.is_empty() {
|
||||
// Oops, the bounds imply there is nothing that can match,
|
||||
// so we always evaluate to false.
|
||||
|
|
@ -521,14 +521,14 @@ impl InternerGuard<'_> {
|
|||
// adjacent ranges map to the same node, which would not be
|
||||
// a canonical representation.
|
||||
if exclude_node_id == first_node_id {
|
||||
let clipped = Range::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
||||
let clipped = Ranges::from_range_bounds((Bound::Unbounded, first_upper.cloned()));
|
||||
*new.first_mut().unwrap() = (clipped, first_node_id);
|
||||
} else {
|
||||
let clipped = Range::from_range_bounds((py_lower.cloned(), first_upper.cloned()));
|
||||
let clipped = Ranges::from_range_bounds((py_lower.cloned(), first_upper.cloned()));
|
||||
*new.first_mut().unwrap() = (clipped, first_node_id);
|
||||
|
||||
let py_range_lower =
|
||||
Range::from_range_bounds((py_lower.cloned(), Bound::Unbounded));
|
||||
Ranges::from_range_bounds((py_lower.cloned(), Bound::Unbounded));
|
||||
new.insert(0, (py_range_lower.complement(), NodeId::FALSE.negate(i)));
|
||||
}
|
||||
}
|
||||
|
|
@ -539,14 +539,14 @@ impl InternerGuard<'_> {
|
|||
// same reasoning applies here: to maintain a canonical
|
||||
// representation.
|
||||
if exclude_node_id == last_node_id {
|
||||
let clipped = Range::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
||||
let clipped = Ranges::from_range_bounds((last_lower.cloned(), Bound::Unbounded));
|
||||
*new.last_mut().unwrap() = (clipped, last_node_id);
|
||||
} else {
|
||||
let clipped = Range::from_range_bounds((last_lower.cloned(), py_upper.cloned()));
|
||||
let clipped = Ranges::from_range_bounds((last_lower.cloned(), py_upper.cloned()));
|
||||
*new.last_mut().unwrap() = (clipped, last_node_id);
|
||||
|
||||
let py_range_upper =
|
||||
Range::from_range_bounds((Bound::Unbounded, py_upper.cloned()));
|
||||
Ranges::from_range_bounds((Bound::Unbounded, py_upper.cloned()));
|
||||
new.push((py_range_upper.complement(), exclude_node_id));
|
||||
}
|
||||
}
|
||||
|
|
@ -688,7 +688,7 @@ pub(crate) enum Edges {
|
|||
// Invariant: All ranges are simple, meaning they can be represented by a bounded
|
||||
// interval without gaps. Additionally, there are at least two edges in the set.
|
||||
Version {
|
||||
edges: SmallVec<(Range<Version>, NodeId)>,
|
||||
edges: SmallVec<(Ranges<Version>, NodeId)>,
|
||||
},
|
||||
// The edges of a string variable, representing a disjoint set of ranges that cover
|
||||
// the output space.
|
||||
|
|
@ -696,7 +696,7 @@ pub(crate) enum Edges {
|
|||
// Invariant: All ranges are simple, meaning they can be represented by a bounded
|
||||
// interval without gaps. Additionally, there are at least two edges in the set.
|
||||
String {
|
||||
edges: SmallVec<(Range<String>, NodeId)>,
|
||||
edges: SmallVec<(Ranges<String>, NodeId)>,
|
||||
},
|
||||
// The edges of a boolean variable, representing the values `true` (the `high` child)
|
||||
// and `false` (the `low` child).
|
||||
|
|
@ -727,13 +727,13 @@ impl Edges {
|
|||
/// This function will panic for the `In` and `Contains` marker operators, which
|
||||
/// should be represented as separate boolean variables.
|
||||
fn from_string(operator: MarkerOperator, value: String) -> Edges {
|
||||
let range: Range<String> = match operator {
|
||||
MarkerOperator::Equal => Range::singleton(value),
|
||||
MarkerOperator::NotEqual => Range::singleton(value).complement(),
|
||||
MarkerOperator::GreaterThan => Range::strictly_higher_than(value),
|
||||
MarkerOperator::GreaterEqual => Range::higher_than(value),
|
||||
MarkerOperator::LessThan => Range::strictly_lower_than(value),
|
||||
MarkerOperator::LessEqual => Range::lower_than(value),
|
||||
let range: Ranges<String> = match operator {
|
||||
MarkerOperator::Equal => Ranges::singleton(value),
|
||||
MarkerOperator::NotEqual => Ranges::singleton(value).complement(),
|
||||
MarkerOperator::GreaterThan => Ranges::strictly_higher_than(value),
|
||||
MarkerOperator::GreaterEqual => Ranges::higher_than(value),
|
||||
MarkerOperator::LessThan => Ranges::strictly_lower_than(value),
|
||||
MarkerOperator::LessEqual => Ranges::lower_than(value),
|
||||
MarkerOperator::TildeEqual => unreachable!("string comparisons with ~= are ignored"),
|
||||
_ => unreachable!("`in` and `contains` are treated as boolean variables"),
|
||||
};
|
||||
|
|
@ -756,7 +756,7 @@ impl Edges {
|
|||
///
|
||||
/// Only for use when the `key` is a `PythonVersion`. Normalizes to `PythonFullVersion`.
|
||||
fn from_python_versions(versions: Vec<Version>, negated: bool) -> Result<Edges, NodeId> {
|
||||
let mut range = Range::empty();
|
||||
let mut range = Ranges::empty();
|
||||
|
||||
// TODO(zanieb): We need to make sure this is performant, repeated unions like this do not
|
||||
// seem efficient.
|
||||
|
|
@ -779,12 +779,12 @@ impl Edges {
|
|||
|
||||
/// Returns an [`Edges`] where values in the given range are `true`.
|
||||
fn from_versions(versions: &Vec<Version>, negated: bool) -> Edges {
|
||||
let mut range = Range::empty();
|
||||
let mut range = Ranges::empty();
|
||||
|
||||
// TODO(zanieb): We need to make sure this is performant, repeated unions like this do not
|
||||
// seem efficient.
|
||||
for version in versions {
|
||||
range = range.union(&Range::singleton(version.clone()));
|
||||
range = range.union(&Ranges::singleton(version.clone()));
|
||||
}
|
||||
|
||||
if negated {
|
||||
|
|
@ -797,7 +797,7 @@ impl Edges {
|
|||
}
|
||||
|
||||
/// Returns an [`Edges`] where values in the given range are `true`.
|
||||
fn from_range<T>(range: &Range<T>) -> SmallVec<(Range<T>, NodeId)>
|
||||
fn from_range<T>(range: &Ranges<T>) -> SmallVec<(Ranges<T>, NodeId)>
|
||||
where
|
||||
T: Ord + Clone,
|
||||
{
|
||||
|
|
@ -805,13 +805,13 @@ impl Edges {
|
|||
|
||||
// Add the `true` edges.
|
||||
for (start, end) in range.iter() {
|
||||
let range = Range::from_range_bounds((start.clone(), end.clone()));
|
||||
let range = Ranges::from_range_bounds((start.clone(), end.clone()));
|
||||
edges.push((range, NodeId::TRUE));
|
||||
}
|
||||
|
||||
// Add the `false` edges.
|
||||
for (start, end) in range.complement().iter() {
|
||||
let range = Range::from_range_bounds((start.clone(), end.clone()));
|
||||
let range = Ranges::from_range_bounds((start.clone(), end.clone()));
|
||||
edges.push((range, NodeId::FALSE));
|
||||
}
|
||||
|
||||
|
|
@ -891,12 +891,12 @@ impl Edges {
|
|||
/// In that case, we drop any ranges that do not exist in the domain of both edges. Note that
|
||||
/// this should not occur in practice because `requires-python` bounds are global.
|
||||
fn apply_ranges<T>(
|
||||
left_edges: &SmallVec<(Range<T>, NodeId)>,
|
||||
left_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||
left_parent: NodeId,
|
||||
right_edges: &SmallVec<(Range<T>, NodeId)>,
|
||||
right_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||
right_parent: NodeId,
|
||||
mut apply: impl FnMut(NodeId, NodeId) -> NodeId,
|
||||
) -> SmallVec<(Range<T>, NodeId)>
|
||||
) -> SmallVec<(Ranges<T>, NodeId)>
|
||||
where
|
||||
T: Clone + Ord,
|
||||
{
|
||||
|
|
@ -968,9 +968,9 @@ impl Edges {
|
|||
|
||||
// Returns `true` if all intersecting ranges in two range maps are disjoint.
|
||||
fn is_disjoint_ranges<T>(
|
||||
left_edges: &SmallVec<(Range<T>, NodeId)>,
|
||||
left_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||
left_parent: NodeId,
|
||||
right_edges: &SmallVec<(Range<T>, NodeId)>,
|
||||
right_edges: &SmallVec<(Ranges<T>, NodeId)>,
|
||||
right_parent: NodeId,
|
||||
interner: &mut InternerGuard<'_>,
|
||||
) -> bool
|
||||
|
|
@ -1179,7 +1179,7 @@ fn python_version_to_full_version(specifier: VersionSpecifier) -> Result<Version
|
|||
}
|
||||
|
||||
/// Compares the start of two ranges that are known to be disjoint.
|
||||
fn compare_disjoint_range_start<T>(range1: &Range<T>, range2: &Range<T>) -> Ordering
|
||||
fn compare_disjoint_range_start<T>(range1: &Ranges<T>, range2: &Ranges<T>) -> Ordering
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
|
|
@ -1199,7 +1199,7 @@ where
|
|||
}
|
||||
|
||||
/// Returns `true` if two disjoint ranges can be conjoined seamlessly without introducing a gap.
|
||||
fn can_conjoin<T>(range1: &Range<T>, range2: &Range<T>) -> bool
|
||||
fn can_conjoin<T>(range1: &Ranges<T>, range2: &Ranges<T>) -> bool
|
||||
where
|
||||
T: Ord + Clone,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ use std::ops::Bound;
|
|||
|
||||
use indexmap::IndexMap;
|
||||
use itertools::Itertools;
|
||||
use pubgrub::Range;
|
||||
use rustc_hash::FxBuildHasher;
|
||||
use uv_pep440::{Version, VersionSpecifier};
|
||||
use version_ranges::Ranges;
|
||||
|
||||
use crate::{ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeKind};
|
||||
|
||||
|
|
@ -280,17 +280,17 @@ fn simplify(dnf: &mut Vec<Vec<MarkerExpression>>) {
|
|||
|
||||
/// Merge any edges that lead to identical subtrees into a single range.
|
||||
pub(crate) fn collect_edges<'a, T>(
|
||||
map: impl ExactSizeIterator<Item = (&'a Range<T>, MarkerTree)>,
|
||||
) -> IndexMap<MarkerTree, Range<T>, FxBuildHasher>
|
||||
map: impl ExactSizeIterator<Item = (&'a Ranges<T>, MarkerTree)>,
|
||||
) -> IndexMap<MarkerTree, Ranges<T>, FxBuildHasher>
|
||||
where
|
||||
T: Ord + Clone + 'a,
|
||||
{
|
||||
let mut paths: IndexMap<_, Range<_>, FxBuildHasher> = IndexMap::default();
|
||||
let mut paths: IndexMap<_, Ranges<_>, FxBuildHasher> = IndexMap::default();
|
||||
for (range, tree) in map {
|
||||
// OK because all ranges are guaranteed to be non-empty.
|
||||
let (start, end) = range.bounding_range().unwrap();
|
||||
// Combine the ranges.
|
||||
let range = Range::from_range_bounds((start.cloned(), end.cloned()));
|
||||
let range = Ranges::from_range_bounds((start.cloned(), end.cloned()));
|
||||
paths
|
||||
.entry(tree)
|
||||
.and_modify(|union| *union = union.union(&range))
|
||||
|
|
@ -305,7 +305,7 @@ where
|
|||
///
|
||||
/// For example, `os_name < 'Linux' or os_name > 'Linux'` can be simplified to
|
||||
/// `os_name != 'Linux'`.
|
||||
fn range_inequality<T>(range: &Range<T>) -> Option<Vec<&T>>
|
||||
fn range_inequality<T>(range: &Ranges<T>) -> Option<Vec<&T>>
|
||||
where
|
||||
T: Ord + Clone + fmt::Debug,
|
||||
{
|
||||
|
|
@ -329,7 +329,7 @@ where
|
|||
///
|
||||
/// For example, `python_full_version < '3.8' or python_full_version >= '3.9'` can be simplified to
|
||||
/// `python_full_version != '3.8.*'`.
|
||||
fn star_range_inequality(range: &Range<Version>) -> Option<VersionSpecifier> {
|
||||
fn star_range_inequality(range: &Ranges<Version>) -> Option<VersionSpecifier> {
|
||||
let (b1, b2) = range.iter().collect_tuple()?;
|
||||
|
||||
match (b1, b2) {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use std::ops::{Bound, Deref};
|
|||
use std::str::FromStr;
|
||||
|
||||
use itertools::Itertools;
|
||||
use pubgrub::Range;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use uv_normalize::ExtraName;
|
||||
use uv_pep440::{Version, VersionParseError, VersionSpecifier};
|
||||
use version_ranges::Ranges;
|
||||
|
||||
use crate::cursor::Cursor;
|
||||
use crate::marker::parse;
|
||||
|
|
@ -1396,7 +1396,7 @@ pub enum MarkerTreeKind<'a> {
|
|||
pub struct VersionMarkerTree<'a> {
|
||||
id: NodeId,
|
||||
key: MarkerValueVersion,
|
||||
map: &'a [(Range<Version>, NodeId)],
|
||||
map: &'a [(Ranges<Version>, NodeId)],
|
||||
}
|
||||
|
||||
impl VersionMarkerTree<'_> {
|
||||
|
|
@ -1406,7 +1406,7 @@ impl VersionMarkerTree<'_> {
|
|||
}
|
||||
|
||||
/// The edges of this node, corresponding to possible output ranges of the given variable.
|
||||
pub fn edges(&self) -> impl ExactSizeIterator<Item = (&Range<Version>, MarkerTree)> + '_ {
|
||||
pub fn edges(&self) -> impl ExactSizeIterator<Item = (&Ranges<Version>, MarkerTree)> + '_ {
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
|
||||
|
|
@ -1432,7 +1432,7 @@ impl Ord for VersionMarkerTree<'_> {
|
|||
pub struct StringMarkerTree<'a> {
|
||||
id: NodeId,
|
||||
key: MarkerValueString,
|
||||
map: &'a [(Range<String>, NodeId)],
|
||||
map: &'a [(Ranges<String>, NodeId)],
|
||||
}
|
||||
|
||||
impl StringMarkerTree<'_> {
|
||||
|
|
@ -1442,7 +1442,7 @@ impl StringMarkerTree<'_> {
|
|||
}
|
||||
|
||||
/// The edges of this node, corresponding to possible output ranges of the given variable.
|
||||
pub fn children(&self) -> impl ExactSizeIterator<Item = (&Range<String>, MarkerTree)> {
|
||||
pub fn children(&self) -> impl ExactSizeIterator<Item = (&Ranges<String>, MarkerTree)> {
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(range, node)| (range, MarkerTree(node.negate(self.id))))
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ uv-pep440 = { workspace = true }
|
|||
|
||||
itertools = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
pubgrub = { workspace = true }
|
||||
version-ranges = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::ops::Bound;
|
||||
|
||||
use itertools::Itertools;
|
||||
use pubgrub::Range;
|
||||
use thiserror::Error;
|
||||
use version_ranges::Ranges;
|
||||
|
||||
use uv_pep440::{Operator, Prerelease, Version, VersionSpecifier, VersionSpecifiers};
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ pub enum PubGrubSpecifierError {
|
|||
|
||||
/// A range of versions that can be used to satisfy a requirement.
|
||||
#[derive(Debug)]
|
||||
pub struct PubGrubSpecifier(Range<Version>);
|
||||
pub struct PubGrubSpecifier(Ranges<Version>);
|
||||
|
||||
impl PubGrubSpecifier {
|
||||
/// Returns an iterator over the bounds of the [`PubGrubSpecifier`].
|
||||
|
|
@ -22,19 +22,19 @@ impl PubGrubSpecifier {
|
|||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Return the bounding [`Range`] of the [`PubGrubSpecifier`].
|
||||
/// Return the bounding [`Ranges`] of the [`PubGrubSpecifier`].
|
||||
pub fn bounding_range(&self) -> Option<(Bound<&Version>, Bound<&Version>)> {
|
||||
self.0.bounding_range()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<Version>> for PubGrubSpecifier {
|
||||
fn from(range: Range<Version>) -> Self {
|
||||
impl From<Ranges<Version>> for PubGrubSpecifier {
|
||||
fn from(range: Ranges<Version>) -> Self {
|
||||
PubGrubSpecifier(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PubGrubSpecifier> for Range<Version> {
|
||||
impl From<PubGrubSpecifier> for Ranges<Version> {
|
||||
/// Convert a PubGrub specifier to a range of versions.
|
||||
fn from(specifier: PubGrubSpecifier) -> Self {
|
||||
specifier.0
|
||||
|
|
@ -50,7 +50,7 @@ impl PubGrubSpecifier {
|
|||
let range = specifiers
|
||||
.iter()
|
||||
.map(Self::from_pep440_specifier)
|
||||
.fold_ok(Range::full(), |range, specifier| {
|
||||
.fold_ok(Ranges::full(), |range, specifier| {
|
||||
range.intersection(&specifier.into())
|
||||
})?;
|
||||
Ok(Self(range))
|
||||
|
|
@ -64,15 +64,15 @@ impl PubGrubSpecifier {
|
|||
let ranges = match specifier.operator() {
|
||||
Operator::Equal => {
|
||||
let version = specifier.version().clone();
|
||||
Range::singleton(version)
|
||||
Ranges::singleton(version)
|
||||
}
|
||||
Operator::ExactEqual => {
|
||||
let version = specifier.version().clone();
|
||||
Range::singleton(version)
|
||||
Ranges::singleton(version)
|
||||
}
|
||||
Operator::NotEqual => {
|
||||
let version = specifier.version().clone();
|
||||
Range::singleton(version).complement()
|
||||
Ranges::singleton(version).complement()
|
||||
}
|
||||
Operator::TildeEqual => {
|
||||
let [rest @ .., last, _] = specifier.version().release() else {
|
||||
|
|
@ -82,38 +82,38 @@ impl PubGrubSpecifier {
|
|||
.with_epoch(specifier.version().epoch())
|
||||
.with_dev(Some(0));
|
||||
let version = specifier.version().clone();
|
||||
Range::from_range_bounds(version..upper)
|
||||
Ranges::from_range_bounds(version..upper)
|
||||
}
|
||||
Operator::LessThan => {
|
||||
let version = specifier.version().clone();
|
||||
if version.any_prerelease() {
|
||||
Range::strictly_lower_than(version)
|
||||
Ranges::strictly_lower_than(version)
|
||||
} else {
|
||||
// Per PEP 440: "The exclusive ordered comparison <V MUST NOT allow a
|
||||
// pre-release of the specified version unless the specified version is itself a
|
||||
// pre-release."
|
||||
Range::strictly_lower_than(version.with_min(Some(0)))
|
||||
Ranges::strictly_lower_than(version.with_min(Some(0)))
|
||||
}
|
||||
}
|
||||
Operator::LessThanEqual => {
|
||||
let version = specifier.version().clone();
|
||||
Range::lower_than(version)
|
||||
Ranges::lower_than(version)
|
||||
}
|
||||
Operator::GreaterThan => {
|
||||
// Per PEP 440: "The exclusive ordered comparison >V MUST NOT allow a post-release of
|
||||
// the given version unless V itself is a post release."
|
||||
let version = specifier.version().clone();
|
||||
if let Some(dev) = version.dev() {
|
||||
Range::higher_than(version.with_dev(Some(dev + 1)))
|
||||
Ranges::higher_than(version.with_dev(Some(dev + 1)))
|
||||
} else if let Some(post) = version.post() {
|
||||
Range::higher_than(version.with_post(Some(post + 1)))
|
||||
Ranges::higher_than(version.with_post(Some(post + 1)))
|
||||
} else {
|
||||
Range::strictly_higher_than(version.with_max(Some(0)))
|
||||
Ranges::strictly_higher_than(version.with_max(Some(0)))
|
||||
}
|
||||
}
|
||||
Operator::GreaterThanEqual => {
|
||||
let version = specifier.version().clone();
|
||||
Range::higher_than(version)
|
||||
Ranges::higher_than(version)
|
||||
}
|
||||
Operator::EqualStar => {
|
||||
let low = specifier.version().clone().with_dev(Some(0));
|
||||
|
|
@ -130,7 +130,7 @@ impl PubGrubSpecifier {
|
|||
*release.last_mut().unwrap() += 1;
|
||||
high = high.with_release(release);
|
||||
}
|
||||
Range::from_range_bounds(low..high)
|
||||
Ranges::from_range_bounds(low..high)
|
||||
}
|
||||
Operator::NotEqualStar => {
|
||||
let low = specifier.version().clone().with_dev(Some(0));
|
||||
|
|
@ -147,7 +147,7 @@ impl PubGrubSpecifier {
|
|||
*release.last_mut().unwrap() += 1;
|
||||
high = high.with_release(release);
|
||||
}
|
||||
Range::from_range_bounds(low..high).complement()
|
||||
Ranges::from_range_bounds(low..high).complement()
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ impl PubGrubSpecifier {
|
|||
let range = specifiers
|
||||
.iter()
|
||||
.map(Self::from_release_specifier)
|
||||
.fold_ok(Range::full(), |range, specifier| {
|
||||
.fold_ok(Ranges::full(), |range, specifier| {
|
||||
range.intersection(&specifier.into())
|
||||
})?;
|
||||
Ok(Self(range))
|
||||
|
|
@ -194,15 +194,15 @@ impl PubGrubSpecifier {
|
|||
let ranges = match specifier.operator() {
|
||||
Operator::Equal => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::singleton(version)
|
||||
Ranges::singleton(version)
|
||||
}
|
||||
Operator::ExactEqual => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::singleton(version)
|
||||
Ranges::singleton(version)
|
||||
}
|
||||
Operator::NotEqual => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::singleton(version).complement()
|
||||
Ranges::singleton(version).complement()
|
||||
}
|
||||
Operator::TildeEqual => {
|
||||
let [rest @ .., last, _] = specifier.version().release() else {
|
||||
|
|
@ -210,23 +210,23 @@ impl PubGrubSpecifier {
|
|||
};
|
||||
let upper = Version::new(rest.iter().chain([&(last + 1)]));
|
||||
let version = specifier.version().only_release();
|
||||
Range::from_range_bounds(version..upper)
|
||||
Ranges::from_range_bounds(version..upper)
|
||||
}
|
||||
Operator::LessThan => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::strictly_lower_than(version)
|
||||
Ranges::strictly_lower_than(version)
|
||||
}
|
||||
Operator::LessThanEqual => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::lower_than(version)
|
||||
Ranges::lower_than(version)
|
||||
}
|
||||
Operator::GreaterThan => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::strictly_higher_than(version)
|
||||
Ranges::strictly_higher_than(version)
|
||||
}
|
||||
Operator::GreaterThanEqual => {
|
||||
let version = specifier.version().only_release();
|
||||
Range::higher_than(version)
|
||||
Ranges::higher_than(version)
|
||||
}
|
||||
Operator::EqualStar => {
|
||||
let low = specifier.version().only_release();
|
||||
|
|
@ -237,7 +237,7 @@ impl PubGrubSpecifier {
|
|||
high = high.with_release(release);
|
||||
high
|
||||
};
|
||||
Range::from_range_bounds(low..high)
|
||||
Ranges::from_range_bounds(low..high)
|
||||
}
|
||||
Operator::NotEqualStar => {
|
||||
let low = specifier.version().only_release();
|
||||
|
|
@ -248,7 +248,7 @@ impl PubGrubSpecifier {
|
|||
high = high.with_release(release);
|
||||
high
|
||||
};
|
||||
Range::from_range_bounds(low..high).complement()
|
||||
Ranges::from_range_bounds(low..high).complement()
|
||||
}
|
||||
};
|
||||
Ok(Self(ranges))
|
||||
|
|
|
|||
Loading…
Reference in New Issue