Skip to content

Commit 27e41b1

Browse files
committed
refactor(dist)!: make ToolchainDesc.channel more strongly typed
1 parent b46ef15 commit 27e41b1

File tree

2 files changed

+89
-21
lines changed

2 files changed

+89
-21
lines changed

src/dist/mod.rs

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ pub enum DistError {
120120

121121
#[derive(Debug, PartialEq)]
122122
struct ParsedToolchainDesc {
123-
channel: String,
123+
channel: Channel,
124124
date: Option<String>,
125125
target: Option<String>,
126126
}
@@ -132,8 +132,7 @@ struct ParsedToolchainDesc {
132132
// are nearly-arbitrary strings.
133133
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
134134
pub struct PartialToolchainDesc {
135-
// Either "nightly", "stable", "beta", or an explicit version number
136-
pub channel: String,
135+
pub channel: Channel,
137136
pub date: Option<String>,
138137
pub target: PartialTargetTriple,
139138
}
@@ -146,12 +145,81 @@ pub struct PartialToolchainDesc {
146145
/// 1.55-x86_64-pc-windows-msvc
147146
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
148147
pub struct ToolchainDesc {
149-
// Either "nightly", "stable", "beta", or an explicit version number
150-
pub channel: String,
148+
pub channel: Channel,
151149
pub date: Option<String>,
152150
pub target: TargetTriple,
153151
}
154152

153+
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
154+
pub enum Channel {
155+
Stable,
156+
Beta,
157+
Nightly,
158+
Version(PartialVersion),
159+
}
160+
161+
impl fmt::Display for Channel {
162+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163+
match self {
164+
Self::Stable => write!(f, "stable"),
165+
Self::Beta => write!(f, "beta"),
166+
Self::Nightly => write!(f, "nightly"),
167+
Self::Version(ver) => write!(f, "{ver}"),
168+
}
169+
}
170+
}
171+
172+
impl FromStr for Channel {
173+
type Err = anyhow::Error;
174+
fn from_str(chan: &str) -> Result<Self> {
175+
match chan {
176+
"stable" => Ok(Self::Stable),
177+
"beta" => Ok(Self::Beta),
178+
"nightly" => Ok(Self::Nightly),
179+
ver => ver.parse().map(Self::Version),
180+
}
181+
}
182+
}
183+
184+
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
185+
pub struct PartialVersion {
186+
pub major: u64,
187+
pub minor: Option<u64>,
188+
pub patch: Option<u64>,
189+
pub pre: semver::Prerelease,
190+
}
191+
192+
impl fmt::Display for PartialVersion {
193+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194+
write!(f, "{}", self.major)?;
195+
if let Some(minor) = self.minor {
196+
write!(f, ".{minor}")?;
197+
}
198+
if let Some(patch) = self.patch {
199+
write!(f, ".{patch}")?;
200+
}
201+
if !self.pre.is_empty() {
202+
write!(f, "-{}", self.pre)?;
203+
}
204+
Ok(())
205+
}
206+
}
207+
208+
impl FromStr for PartialVersion {
209+
type Err = anyhow::Error;
210+
fn from_str(ver: &str) -> Result<Self> {
211+
let (ver, pre) = ver.split_once('-').unwrap_or((ver, ""));
212+
let comparator =
213+
semver::Comparator::from_str(ver).context("error parsing `PartialVersion`")?;
214+
Ok(Self {
215+
major: comparator.major,
216+
minor: comparator.minor,
217+
patch: comparator.patch,
218+
pre: semver::Prerelease::new(pre).context("error parsing `PartialVersion`")?,
219+
})
220+
}
221+
}
222+
155223
#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
156224
#[serde(transparent)]
157225
pub struct TargetTriple(String);
@@ -229,7 +297,7 @@ impl FromStr for ParsedToolchainDesc {
229297
};
230298

231299
Self {
232-
channel: channel.to_owned(),
300+
channel: Channel::from_str(channel).unwrap(),
233301
date: c.get(2).map(|s| s.as_str()).and_then(fn_map),
234302
target: c.get(3).map(|s| s.as_str()).and_then(fn_map),
235303
}
@@ -579,7 +647,7 @@ impl ToolchainDesc {
579647
/// Either "$channel" or "channel-$date"
580648
pub fn manifest_name(&self) -> String {
581649
match self.date {
582-
None => self.channel.clone(),
650+
None => self.channel.to_string(),
583651
Some(ref date) => format!("{}-{}", self.channel, date),
584652
}
585653
}
@@ -595,11 +663,11 @@ impl ToolchainDesc {
595663
/// such as `stable`, or is an incomplete version such as `1.48`, and the
596664
/// date field is empty.
597665
pub(crate) fn is_tracking(&self) -> bool {
598-
let channels = ["nightly", "beta", "stable"];
599-
static TRACKING_VERSION: Lazy<Regex> =
600-
Lazy::new(|| Regex::new(r"^\d{1}\.\d{1,3}$").unwrap());
601-
(channels.iter().any(|x| *x == self.channel) || TRACKING_VERSION.is_match(&self.channel))
602-
&& self.date.is_none()
666+
match &self.channel {
667+
_ if self.date.is_some() => false,
668+
Channel::Stable | Channel::Beta | Channel::Nightly => true,
669+
Channel::Version(ver) => ver.patch.is_none() || &*ver.pre == "beta",
670+
}
603671
}
604672
}
605673

@@ -752,7 +820,7 @@ pub(crate) async fn update_from_dist(
752820

753821
let mut fetched = String::new();
754822
let mut first_err = None;
755-
let backtrack = opts.toolchain.channel == "nightly" && opts.toolchain.date.is_none();
823+
let backtrack = opts.toolchain.channel == Channel::Nightly && opts.toolchain.date.is_none();
756824
// We want to limit backtracking if we do not already have a toolchain
757825
let mut backtrack_limit: Option<i32> = if opts.toolchain.date.is_some() {
758826
None
@@ -1099,13 +1167,10 @@ async fn dl_v1_manifest(
10991167
) -> Result<Vec<String>> {
11001168
let root_url = toolchain.package_dir(download.dist_root);
11011169

1102-
if !["nightly", "beta", "stable"].contains(&&*toolchain.channel) {
1170+
if let Channel::Version(ver) = &toolchain.channel {
11031171
// This is an explicit version. In v1 there was no manifest,
11041172
// you just know the file to download, so synthesize one.
1105-
let installer_name = format!(
1106-
"{}/rust-{}-{}.tar.gz",
1107-
root_url, toolchain.channel, toolchain.target
1108-
);
1173+
let installer_name = format!("{}/rust-{}-{}.tar.gz", root_url, ver, toolchain.target);
11091174
return Ok(vec![installer_name]);
11101175
}
11111176

@@ -1191,7 +1256,7 @@ mod tests {
11911256
);
11921257

11931258
let expected = ParsedToolchainDesc {
1194-
channel: channel.into(),
1259+
channel: Channel::from_str(channel).unwrap(),
11951260
date: date.map(String::from),
11961261
target: target.map(String::from),
11971262
};
@@ -1226,6 +1291,9 @@ mod tests {
12261291
("nightly-2020-10-04", false),
12271292
("1.48", true),
12281293
("1.47.0", false),
1294+
("1.23-beta", true),
1295+
("1.23.0-beta", true),
1296+
("1.23.0-beta.2", false),
12291297
];
12301298
for case in CASES {
12311299
let full_tcn = format!("{}-x86_64-unknown-linux-gnu", case.0);

src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use url::Url;
1313
use crate::{
1414
dist::{
1515
manifest::{Component, Manifest},
16-
{TargetTriple, ToolchainDesc},
16+
Channel, TargetTriple, ToolchainDesc,
1717
},
1818
toolchain::{PathBasedToolchainName, ToolchainName},
1919
};
@@ -97,7 +97,7 @@ pub enum RustupError {
9797
ToolchainNotSelected(String),
9898
#[error("toolchain '{}' does not contain component {}{}{}", .desc, .component, suggest_message(.suggestion), if .component.contains("rust-std") {
9999
format!("\nnote: not all platforms have the standard library pre-compiled: https://doc.rust-lang.org/nightly/rustc/platform-support.html{}",
100-
if desc.channel == "nightly" { "\nhelp: consider using `cargo build -Z build-std` instead" } else { "" }
100+
if desc.channel == Channel::Nightly { "\nhelp: consider using `cargo build -Z build-std` instead" } else { "" }
101101
)
102102
} else { "".to_string() })]
103103
UnknownComponent {

0 commit comments

Comments
 (0)