1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::fmt::{self, Display};
use semver::{Op, Version, VersionReq};
use crate::util_semver::VersionExt as _;
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum OptVersionReq {
Any,
Req(VersionReq),
/// The exact locked version and the original version requirement.
Locked(Version, VersionReq),
/// The exact requested version and the original version requirement.
UpdatePrecise(Version, VersionReq),
}
impl OptVersionReq {
pub fn exact(version: &Version) -> Self {
OptVersionReq::Req(version.to_exact_req())
}
// Since some registries have allowed crate versions to differ only by build metadata,
// A query using OptVersionReq::exact return nondeterministic results.
// So we `lock_to` the exact version were interested in.
pub fn lock_to_exact(version: &Version) -> Self {
OptVersionReq::Locked(version.clone(), version.to_exact_req())
}
pub fn is_exact(&self) -> bool {
match self {
OptVersionReq::Any => false,
OptVersionReq::Req(req) | OptVersionReq::UpdatePrecise(_, req) => {
req.comparators.len() == 1 && {
let cmp = &req.comparators[0];
cmp.op == Op::Exact && cmp.minor.is_some() && cmp.patch.is_some()
}
}
OptVersionReq::Locked(..) => true,
}
}
pub fn lock_to(&mut self, version: &Version) {
assert!(self.matches(version), "cannot lock {} to {}", self, version);
use OptVersionReq::*;
let version = version.clone();
*self = match self {
Any => Locked(version, VersionReq::STAR),
Req(req) | Locked(_, req) | UpdatePrecise(_, req) => Locked(version, req.clone()),
};
}
pub fn update_precise(&mut self, version: &Version) {
use OptVersionReq::*;
let version = version.clone();
*self = match self {
Any => UpdatePrecise(version, VersionReq::STAR),
Req(req) | Locked(_, req) | UpdatePrecise(_, req) => {
UpdatePrecise(version, req.clone())
}
};
}
pub fn is_locked(&self) -> bool {
matches!(self, OptVersionReq::Locked(..))
}
/// Gets the version to which this req is locked, if any.
pub fn locked_version(&self) -> Option<&Version> {
match self {
OptVersionReq::Locked(version, _) => Some(version),
_ => None,
}
}
pub fn matches(&self, version: &Version) -> bool {
match self {
OptVersionReq::Any => true,
OptVersionReq::Req(req) => req.matches(version),
OptVersionReq::Locked(v, _) => {
// Generally, cargo is of the opinion that semver metadata should be ignored.
// If your registry has two versions that only differing metadata you get the bugs you deserve.
// We also believe that lock files should ensure reproducibility
// and protect against mutations from the registry.
// In this circumstance these two goals are in conflict, and we pick reproducibility.
// If the lock file tells us that there is a version called `1.0.0+bar` then
// we should not silently use `1.0.0+foo` even though they have the same version.
v == version
}
OptVersionReq::UpdatePrecise(v, _) => {
// This is used for the `--precise` field of cargo update.
//
// Unfortunately crates.io allowed versions to differ only
// by build metadata. This shouldn't be allowed, but since
// it is, this will honor it if requested.
//
// In that context we treat a requirement that does not have
// build metadata as allowing any metadata. But, if a requirement
// has build metadata, then we only allow it to match the exact
// metadata.
v.major == version.major
&& v.minor == version.minor
&& v.patch == version.patch
&& v.pre == version.pre
&& (v.build == version.build || v.build.is_empty())
}
}
}
}
impl Display for OptVersionReq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OptVersionReq::Any => f.write_str("*"),
OptVersionReq::Req(req)
| OptVersionReq::Locked(_, req)
| OptVersionReq::UpdatePrecise(_, req) => Display::fmt(req, f),
}
}
}
impl From<VersionReq> for OptVersionReq {
fn from(req: VersionReq) -> Self {
OptVersionReq::Req(req)
}
}