Skip to content

Commit

Permalink
Consider rust-version when selecting packages for cargo add
Browse files Browse the repository at this point in the history
When `-Zmsrv-policy` is enabled, try to select dependencies which satisfy the
target package's `rust-version` field (if present). If the selected version is
not also the latest, emit a warning to the user about this discrepancy.

Dependency versions without a `rust-version` are considered compatible by
default.

Implements rust-lang#10653.
  • Loading branch information
cassaundra committed May 2, 2023
1 parent ac84010 commit bc5ce62
Showing 1 changed file with 60 additions and 11 deletions.
71 changes: 60 additions & 11 deletions src/cargo/ops/cargo_add/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ fn resolve_dependency(
}
dependency = dependency.set_source(src);
} else {
let latest = get_latest_dependency(&dependency, false, config, registry)?;
let latest = get_latest_dependency(manifest, &dependency, false, config, registry)?;

if dependency.name != latest.name {
config.shell().warn(format!(
Expand Down Expand Up @@ -518,6 +518,7 @@ fn get_existing_dependency(
}

fn get_latest_dependency(
manifest: &LocalManifest,
dependency: &Dependency,
_flag_allow_prerelease: bool,
config: &Config,
Expand All @@ -537,19 +538,67 @@ fn get_latest_dependency(
std::task::Poll::Pending => registry.block_until_ready()?,
}
};
let latest = possibilities
.iter()
.max_by_key(|s| {
// Fallback to a pre-release if no official release is available by sorting them as
// less.

fn find_latest<'a>(
possibilities: impl Iterator<Item = &'a Summary>,
) -> Option<&'a Summary> {
possibilities.max_by_key(|s| {
// Fallback to a pre-release if no official release is available by sorting them
// as less.
let stable = s.version().pre.is_empty();
(stable, s.version())
})
.ok_or_else(|| {
anyhow::format_err!(
"the crate `{dependency}` could not be found in registry index."
)
})?;
}

let mut latest = find_latest(possibilities.iter()).ok_or_else(|| {
anyhow::format_err!(
"the crate `{dependency}` could not be found in registry index."
)
})?;

if config.cli_unstable().msrv_policy {
if let Some(rust_version) = manifest
.data
.as_table()
.get("package")
.and_then(|p| p.get("rust-version"))
.and_then(|v| v.as_str())
.and_then(|v| semver::VersionReq::parse(v).ok())
{
// Find the latest version of the dep which has a compatible MSRV. Candidates
// without a rust-version field are treated as compatible.
let latest_msrv = find_latest(possibilities.iter().filter(|s| {
s.rust_version()
.map(|v| v.as_str())
.map(|v| semver::VersionReq::parse(v).unwrap())
.map(|v| {
// Rust versions need at least a major and a minor value, and cannot
// contain semver operators or pre-release identifiers, so the
// comparison is simple.
let required = &rust_version.comparators[0];
let other = &v.comparators[0];
(required.major, required.minor.unwrap(), required.patch)
>= (other.major, other.minor.unwrap(), other.patch)
})
.unwrap_or(true)
}))
.ok_or_else(|| {
anyhow::format_err!(
"could not find version of crate `{dependency}` that satisfies the \
minimum supported rust version"
)
})?;

if latest_msrv.version() < latest.version() {
config.shell().warn(format!(
"selecting older version of `{dependency}` to satisfy the minimum \
supported rust version"
))?;
latest = latest_msrv;
}
}
}

let mut dep = Dependency::from(latest);
if let Some(reg_name) = dependency.registry.as_deref() {
dep = dep.set_registry(reg_name);
Expand Down

0 comments on commit bc5ce62

Please sign in to comment.