diff --git a/src/cargo/core/source/source_id.rs b/src/cargo/core/source/source_id.rs index e5fe4483f9f..4c7de19148d 100644 --- a/src/cargo/core/source/source_id.rs +++ b/src/cargo/core/source/source_id.rs @@ -2,7 +2,8 @@ use crate::core::PackageId; use crate::sources::registry::CRATES_IO_HTTP_INDEX; use crate::sources::{DirectorySource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY}; use crate::sources::{GitSource, PathSource, RegistrySource}; -use crate::util::{config, CanonicalUrl, CargoResult, Config, IntoUrl}; +use crate::util::{config, CanonicalUrl, CargoResult, Config, IntoUrl, ToSemver}; +use anyhow::Context; use serde::de; use serde::ser; use std::cmp::{self, Ordering}; @@ -430,11 +431,6 @@ impl SourceId { } } - /// Gets the value of the precise field. - pub fn precise(self) -> Option<&'static str> { - self.inner.precise.as_deref() - } - /// Gets the Git reference if this is a git source, otherwise `None`. pub fn git_reference(self) -> Option<&'static GitReference> { match self.inner.kind { @@ -443,6 +439,33 @@ impl SourceId { } } + /// Gets the value of the precise field. + pub fn precise(self) -> Option<&'static str> { + self.inner.precise.as_deref() + } + + /// Check if the precise data field stores information for this `name` + /// from a call to [SourceId::with_precise_registry_version]. + /// + /// If so return the version currently in the lock file and the version to be updated to. + /// If specified, our own source will have a precise version listed of the form + // `=->` where `` is the name of a crate on + // this source, `` is the version installed and `` is the + // version requested (argument to `--precise`). + pub fn precise_registry_version( + self, + name: &str, + ) -> Option<(semver::Version, semver::Version)> { + self.inner + .precise + .as_deref() + .and_then(|p| p.strip_prefix(name)?.strip_prefix('=')) + .map(|p| { + let (current, requested) = p.split_once("->").unwrap(); + (current.to_semver().unwrap(), requested.to_semver().unwrap()) + }) + } + /// Creates a new `SourceId` from this source with the given `precise`. pub fn with_precise(self, v: Option) -> SourceId { SourceId::wrap(SourceIdInner { @@ -451,6 +474,23 @@ impl SourceId { }) } + /// When updating a lock file on a version using `cargo update --precise` + /// the requested version is stored in the precise field. + /// On a registry dependency we also need to keep track of the package that + /// should be updated and even which of the versions should be updated. + /// All of this gets encoded in the precise field using this method. + /// The data can be read with [SourceId::precise_registry_version] + pub fn with_precise_registry_version( + self, + name: impl fmt::Display, + version: &semver::Version, + precise: &str, + ) -> CargoResult { + semver::Version::parse(precise) + .with_context(|| format!("invalid version format for precise version `{precise}`"))?; + Ok(self.with_precise(Some(format!("{}={}->{}", name, version, precise)))) + } + /// Returns `true` if the remote registry is the standard . pub fn is_crates_io(self) -> bool { match self.inner.kind { diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs index 265d2a883d4..5e6fda7551d 100644 --- a/src/cargo/ops/cargo_generate_lockfile.rs +++ b/src/cargo/ops/cargo_generate_lockfile.rs @@ -5,7 +5,6 @@ use crate::core::{Resolve, SourceId, Workspace}; use crate::ops; use crate::util::config::Config; use crate::util::CargoResult; -use anyhow::Context; use std::collections::{BTreeMap, HashSet}; use termcolor::Color::{self, Cyan, Green, Red, Yellow}; use tracing::debug; @@ -88,27 +87,27 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes } else { let mut sources = Vec::new(); for name in opts.to_update.iter() { - let dep = previous_resolve.query(name)?; + let pid = previous_resolve.query(name)?; if opts.recursive { - fill_with_deps(&previous_resolve, dep, &mut to_avoid, &mut HashSet::new()); + fill_with_deps(&previous_resolve, pid, &mut to_avoid, &mut HashSet::new()); } else { - to_avoid.insert(dep); + to_avoid.insert(pid); sources.push(match opts.precise { Some(precise) => { // TODO: see comment in `resolve.rs` as well, but this // seems like a pretty hokey reason to single out // the registry as well. - let precise = if dep.source_id().is_registry() { - semver::Version::parse(precise).with_context(|| { - format!("invalid version format for precise version `{}`", precise) - })?; - format!("{}={}->{}", dep.name(), dep.version(), precise) + if pid.source_id().is_registry() { + pid.source_id().with_precise_registry_version( + pid.name(), + pid.version(), + precise, + )? } else { - precise.to_string() - }; - dep.source_id().with_precise(Some(precise)) + pid.source_id().with_precise(Some(precise.to_string())) + } } - None => dep.source_id().with_precise(None), + None => pid.source_id().with_precise(None), }); } if let Ok(unused_id) = diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index cc9e18735dc..ec3650b38d1 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -91,9 +91,7 @@ use crate::core::{PackageId, SourceId, Summary}; use crate::sources::registry::{LoadResponse, RegistryData}; use crate::util::interning::InternedString; use crate::util::IntoUrl; -use crate::util::{ - internal, CargoResult, Config, Filesystem, OptVersionReq, PartialVersion, ToSemver, -}; +use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq, PartialVersion}; use anyhow::bail; use cargo_util::{paths, registry::make_dep_path}; use semver::Version; @@ -582,18 +580,8 @@ impl<'cfg> RegistryIndex<'cfg> { .filter(|s| !s.yanked || yanked_whitelist.contains(&s.summary.package_id())) .map(|s| s.summary.clone()); - // Handle `cargo update --precise` here. If specified, our own source - // will have a precise version listed of the form - // `=o->` where `` is the name of a crate on - // this source, `` is the version installed and ` is the - // version requested (argument to `--precise`). - let precise = source_id - .precise() - .filter(|p| p.starts_with(name) && p[name.len()..].starts_with('=')) - .map(|p| { - let (current, requested) = p[name.len() + 1..].split_once("->").unwrap(); - (current.to_semver().unwrap(), requested.to_semver().unwrap()) - }); + // Handle `cargo update --precise` here. + let precise = source_id.precise_registry_version(name); let summaries = summaries.filter(|s| match &precise { Some((current, requested)) => { if req.matches(current) {