diff --git a/src/cargo/ops/cargo_add/mod.rs b/src/cargo/ops/cargo_add/mod.rs index 5c519ac09f0c..cd94b7b7fb42 100644 --- a/src/cargo/ops/cargo_add/mod.rs +++ b/src/cargo/ops/cargo_add/mod.rs @@ -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!( @@ -518,6 +518,7 @@ fn get_existing_dependency( } fn get_latest_dependency( + manifest: &LocalManifest, dependency: &Dependency, _flag_allow_prerelease: bool, config: &Config, @@ -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, + ) -> 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 req = &rust_version.comparators[0]; + let dep = &v.comparators[0]; + (req.major, req.minor.unwrap(), req.patch.unwrap_or(0)) + >= (dep.major, dep.minor.unwrap(), dep.patch.unwrap_or(0)) + }) + .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);