Start using the version ranges crate (#8667)

This commit is contained in:
konsti 2024-10-29 17:39:50 +01:00 committed by GitHub
parent a56e521179
commit 36102dbd0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 92 additions and 82 deletions

15
Cargo.lock generated
View File

@ -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"

View File

@ -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" }

View File

@ -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" }

View File

@ -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,
{

View File

@ -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) {

View File

@ -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))))

View File

@ -15,4 +15,4 @@ uv-pep440 = { workspace = true }
itertools = { workspace = true }
thiserror = { workspace = true }
pubgrub = { workspace = true }
version-ranges = { workspace = true }

View File

@ -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))