diff --git a/src/rustup.rs b/src/rustup.rs index e56e8c00..058a2f98 100644 --- a/src/rustup.rs +++ b/src/rustup.rs @@ -5,7 +5,7 @@ use anyhow::{bail, format_err, Result}; use crate::{ cargo, context::Context, - version::{Version, VersionRange}, + version::{MaybeVersion, Version, VersionRange}, }; pub(crate) struct Rustup { @@ -41,39 +41,65 @@ pub(crate) fn version_range( Ok(()) }; - let VersionRange { start_inclusive, end_inclusive } = range; + let mut stable_version = None; + let mut get_stable_version = || -> Result { + if let Some(stable_version) = stable_version { + Ok(stable_version) + } else { + install_toolchain("stable", &[], false)?; + let version = cargo::version(cmd!("cargo", "+stable"))?; + stable_version = Some(version); + Ok(version) + } + }; - let start_inclusive = match start_inclusive { - Some(start) => start, - None => { - let mut rust_version = None; + let mut rust_version = None; + let mut get_rust_version = || -> Result { + if let Some(rust_version) = rust_version { + Ok(rust_version) + } else { + let mut version = None; for id in cx.workspace_members() { let v = cx.rust_version(id); - if v.is_none() || v == rust_version { + if v.is_none() || v == version { // no-op - } else if rust_version.is_none() { - rust_version = v; + } else if version.is_none() { + version = v; } else { bail!("automatic detection of the lower bound of the version range is not yet supported when the minimum supported Rust version of the crates in the workspace do not match") } } - match rust_version { + let version = match version { Some(v) => v.parse()?, None => bail!("no rust-version field in Cargo.toml is specified"), - } + }; + rust_version = Some(version); + Ok(version) } }; - check(&start_inclusive)?; + + let VersionRange { start_inclusive, end_inclusive } = range; + + let start_inclusive = match start_inclusive { + MaybeVersion::Version(start) => { + check(&start)?; + start + } + MaybeVersion::Msrv => { + let start = get_rust_version()?; + check(&start)?; + start + } + MaybeVersion::Stable => get_stable_version()?, + }; let end_inclusive = match end_inclusive { - Some(end) => { + MaybeVersion::Version(end) => { check(&end)?; end } - None => { - install_toolchain("stable", &[], false)?; - cargo::version(cmd!("cargo", "+stable"))? - } + MaybeVersion::Msrv => get_rust_version()?, + MaybeVersion::Stable => get_stable_version()?, }; let step = step.map(str::parse::).transpose()?.unwrap_or(1); diff --git a/src/version.rs b/src/version.rs index 27e48a84..74604d99 100644 --- a/src/version.rs +++ b/src/version.rs @@ -33,19 +33,26 @@ impl FromStr for Version { } } +#[derive(Copy, Clone)] +pub(crate) enum MaybeVersion { + Version(Version), + Msrv, + Stable, +} + #[derive(Copy, Clone)] pub(crate) struct VersionRange { - pub(crate) start_inclusive: Option, - pub(crate) end_inclusive: Option, + pub(crate) start_inclusive: MaybeVersion, + pub(crate) end_inclusive: MaybeVersion, } impl fmt::Display for VersionRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(start) = self.start_inclusive { + if let MaybeVersion::Version(start) = self.start_inclusive { write!(f, "{start}")?; } write!(f, "..=")?; - if let Some(end) = self.end_inclusive { + if let MaybeVersion::Version(end) = self.end_inclusive { write!(f, "{end}")?; } Ok(()) @@ -56,7 +63,7 @@ impl FromStr for VersionRange { type Err = Error; fn from_str(s: &str) -> Result { - let (start, end_inclusive) = if let Some((start, end)) = s.split_once("..") { + let (start, end) = if let Some((start, end)) = s.split_once("..") { let end = match end.strip_prefix('=') { Some(end) => end, None => { @@ -71,15 +78,16 @@ impl FromStr for VersionRange { } else { (s, None) }; - let start_inclusive = maybe_version(start)?; + let start_inclusive = maybe_version(start)?.unwrap_or(MaybeVersion::Msrv); + let end_inclusive = end.unwrap_or(MaybeVersion::Stable); Ok(Self { start_inclusive, end_inclusive }) } } -fn maybe_version(s: &str) -> Result, Error> { +fn maybe_version(s: &str) -> Result, Error> { if s.is_empty() { Ok(None) } else { - s.parse().map(Some) + s.parse().map(MaybeVersion::Version).map(Some) } }