Skip to content

Commit 2b2502f

Browse files
committed
refactor(util): Pull out mod util_semver
This `mod` is a proposal for what a new package would look like. This needs to be split out so a future `util_manifest_schema` package can depend on it (rust-lang#12801). This doesn't address where `RustVersion` should live (along with `PackageIdSpec`). This builds on the work from rust-lang#12924 and rust-lang#12926
1 parent 8cf7143 commit 2b2502f

File tree

7 files changed

+207
-197
lines changed

7 files changed

+207
-197
lines changed

src/bin/cargo/commands/install.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use anyhow::format_err;
66
use cargo::core::{GitReference, SourceId, Workspace};
77
use cargo::ops;
88
use cargo::util::IntoUrl;
9-
use cargo::util::VersionExt;
9+
use cargo::util_semver::VersionExt;
1010
use cargo::CargoResult;
1111
use itertools::Itertools;
1212
use semver::VersionReq;

src/cargo/core/package_id_spec.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use url::Url;
99
use crate::core::PackageId;
1010
use crate::util::edit_distance;
1111
use crate::util::errors::CargoResult;
12-
use crate::util::PartialVersion;
1312
use crate::util::{validate_package_name, IntoUrl};
13+
use crate::util_semver::PartialVersion;
1414

1515
/// Some or all of the data required to identify a package:
1616
///

src/cargo/core/resolver/errors.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use std::task::Poll;
44
use crate::core::{Dependency, PackageId, Registry, Summary};
55
use crate::sources::source::QueryKind;
66
use crate::util::edit_distance::edit_distance;
7-
use crate::util::{Config, OptVersionReq, VersionExt};
7+
use crate::util::{Config, OptVersionReq};
8+
use crate::util_semver::VersionExt;
89
use anyhow::Error;
910

1011
use super::context::Context;

src/cargo/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub mod core;
161161
pub mod ops;
162162
pub mod sources;
163163
pub mod util;
164+
pub mod util_semver;
164165
mod version;
165166

166167
pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {

src/cargo/util/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use self::progress::{Progress, ProgressStyle};
2323
pub use self::queue::Queue;
2424
pub use self::restricted_names::validate_package_name;
2525
pub use self::rustc::Rustc;
26-
pub use self::semver_ext::{OptVersionReq, PartialVersion, RustVersion, VersionExt};
26+
pub use self::semver_ext::{OptVersionReq, RustVersion};
2727
pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
2828
pub use self::workspace::{
2929
add_path_args, path_args, print_available_benches, print_available_binaries,

src/cargo/util/semver_ext.rs

+6-193
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
use semver::{Comparator, Op, Version, VersionReq};
2-
use serde_untagged::UntaggedEnumVisitor;
31
use std::fmt::{self, Display};
42

3+
use semver::{Op, Version, VersionReq};
4+
use serde_untagged::UntaggedEnumVisitor;
5+
6+
use crate::util_semver::PartialVersion;
7+
use crate::util_semver::VersionExt as _;
8+
59
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
610
pub enum OptVersionReq {
711
Any,
@@ -12,30 +16,6 @@ pub enum OptVersionReq {
1216
UpdatePrecise(Version, VersionReq),
1317
}
1418

15-
pub trait VersionExt {
16-
fn is_prerelease(&self) -> bool;
17-
18-
fn to_exact_req(&self) -> VersionReq;
19-
}
20-
21-
impl VersionExt for Version {
22-
fn is_prerelease(&self) -> bool {
23-
!self.pre.is_empty()
24-
}
25-
26-
fn to_exact_req(&self) -> VersionReq {
27-
VersionReq {
28-
comparators: vec![Comparator {
29-
op: Op::Exact,
30-
major: self.major,
31-
minor: Some(self.minor),
32-
patch: Some(self.patch),
33-
pre: self.pre.clone(),
34-
}],
35-
}
36-
}
37-
}
38-
3919
impl OptVersionReq {
4020
pub fn exact(version: &Version) -> Self {
4121
OptVersionReq::Req(version.to_exact_req())
@@ -190,170 +170,3 @@ impl Display for RustVersion {
190170
self.0.fmt(f)
191171
}
192172
}
193-
194-
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
195-
pub struct PartialVersion {
196-
pub major: u64,
197-
pub minor: Option<u64>,
198-
pub patch: Option<u64>,
199-
pub pre: Option<semver::Prerelease>,
200-
pub build: Option<semver::BuildMetadata>,
201-
}
202-
203-
impl PartialVersion {
204-
pub fn to_version(&self) -> Option<Version> {
205-
Some(Version {
206-
major: self.major,
207-
minor: self.minor?,
208-
patch: self.patch?,
209-
pre: self.pre.clone().unwrap_or_default(),
210-
build: self.build.clone().unwrap_or_default(),
211-
})
212-
}
213-
214-
pub fn to_caret_req(&self) -> VersionReq {
215-
VersionReq {
216-
comparators: vec![Comparator {
217-
op: semver::Op::Caret,
218-
major: self.major,
219-
minor: self.minor,
220-
patch: self.patch,
221-
pre: self.pre.as_ref().cloned().unwrap_or_default(),
222-
}],
223-
}
224-
}
225-
226-
/// Check if this matches a version, including build metadata
227-
///
228-
/// Build metadata does not affect version precedence but may be necessary for uniquely
229-
/// identifying a package.
230-
pub fn matches(&self, version: &Version) -> bool {
231-
if !version.pre.is_empty() && self.pre.is_none() {
232-
// Pre-release versions must be explicitly opted into, if for no other reason than to
233-
// give us room to figure out and define the semantics
234-
return false;
235-
}
236-
self.major == version.major
237-
&& self.minor.map(|f| f == version.minor).unwrap_or(true)
238-
&& self.patch.map(|f| f == version.patch).unwrap_or(true)
239-
&& self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
240-
&& self
241-
.build
242-
.as_ref()
243-
.map(|f| f == &version.build)
244-
.unwrap_or(true)
245-
}
246-
}
247-
248-
impl From<semver::Version> for PartialVersion {
249-
fn from(ver: semver::Version) -> Self {
250-
let pre = if ver.pre.is_empty() {
251-
None
252-
} else {
253-
Some(ver.pre)
254-
};
255-
let build = if ver.build.is_empty() {
256-
None
257-
} else {
258-
Some(ver.build)
259-
};
260-
Self {
261-
major: ver.major,
262-
minor: Some(ver.minor),
263-
patch: Some(ver.patch),
264-
pre,
265-
build,
266-
}
267-
}
268-
}
269-
270-
impl std::str::FromStr for PartialVersion {
271-
type Err = anyhow::Error;
272-
273-
fn from_str(value: &str) -> Result<Self, Self::Err> {
274-
if is_req(value) {
275-
anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"")
276-
}
277-
match semver::Version::parse(value) {
278-
Ok(ver) => Ok(ver.into()),
279-
Err(_) => {
280-
// HACK: Leverage `VersionReq` for partial version parsing
281-
let mut version_req = match semver::VersionReq::parse(value) {
282-
Ok(req) => req,
283-
Err(_) if value.contains('-') => {
284-
anyhow::bail!(
285-
"unexpected prerelease field, expected a version like \"1.32\""
286-
)
287-
}
288-
Err(_) if value.contains('+') => {
289-
anyhow::bail!("unexpected build field, expected a version like \"1.32\"")
290-
}
291-
Err(_) => anyhow::bail!("expected a version like \"1.32\""),
292-
};
293-
assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req");
294-
let comp = version_req.comparators.pop().unwrap();
295-
assert_eq!(comp.op, semver::Op::Caret, "guaranteed by is_req");
296-
let pre = if comp.pre.is_empty() {
297-
None
298-
} else {
299-
Some(comp.pre)
300-
};
301-
Ok(Self {
302-
major: comp.major,
303-
minor: comp.minor,
304-
patch: comp.patch,
305-
pre,
306-
build: None,
307-
})
308-
}
309-
}
310-
}
311-
}
312-
313-
impl Display for PartialVersion {
314-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315-
let major = self.major;
316-
write!(f, "{major}")?;
317-
if let Some(minor) = self.minor {
318-
write!(f, ".{minor}")?;
319-
}
320-
if let Some(patch) = self.patch {
321-
write!(f, ".{patch}")?;
322-
}
323-
if let Some(pre) = self.pre.as_ref() {
324-
write!(f, "-{pre}")?;
325-
}
326-
if let Some(build) = self.build.as_ref() {
327-
write!(f, "+{build}")?;
328-
}
329-
Ok(())
330-
}
331-
}
332-
333-
impl serde::Serialize for PartialVersion {
334-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
335-
where
336-
S: serde::Serializer,
337-
{
338-
serializer.collect_str(self)
339-
}
340-
}
341-
342-
impl<'de> serde::Deserialize<'de> for PartialVersion {
343-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
344-
where
345-
D: serde::Deserializer<'de>,
346-
{
347-
UntaggedEnumVisitor::new()
348-
.expecting("SemVer version")
349-
.string(|value| value.parse().map_err(serde::de::Error::custom))
350-
.deserialize(deserializer)
351-
}
352-
}
353-
354-
fn is_req(value: &str) -> bool {
355-
let Some(first) = value.chars().next() else {
356-
return false;
357-
};
358-
"<>=^~".contains(first) || value.contains('*') || value.contains(',')
359-
}

0 commit comments

Comments
 (0)