Skip to content

Commit 0227114

Browse files
committed
Consider public dependencies when choosing a version in cargo add (#13038)
1 parent 75768e0 commit 0227114

File tree

3 files changed

+133
-7
lines changed

3 files changed

+133
-7
lines changed

src/cargo/ops/cargo_add/mod.rs

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,20 @@ use toml_edit::Item as TomlItem;
2020

2121
use crate::CargoResult;
2222
use crate::GlobalContext;
23+
use crate::core::Feature;
2324
use crate::core::FeatureValue;
2425
use crate::core::Features;
2526
use crate::core::Package;
27+
use crate::core::PackageId;
2628
use crate::core::Registry;
2729
use crate::core::Shell;
2830
use crate::core::Summary;
2931
use crate::core::Workspace;
3032
use crate::core::dependency::DepKind;
3133
use crate::core::registry::PackageRegistry;
34+
use crate::ops::resolve_ws;
3235
use crate::sources::source::QueryKind;
36+
use crate::util::OptVersionReq;
3337
use crate::util::cache_lock::CacheLockMode;
3438
use crate::util::edit_distance;
3539
use crate::util::style;
@@ -38,6 +42,7 @@ use crate::util::toml_mut::dependency::Dependency;
3842
use crate::util::toml_mut::dependency::GitSource;
3943
use crate::util::toml_mut::dependency::MaybeWorkspace;
4044
use crate::util::toml_mut::dependency::PathSource;
45+
use crate::util::toml_mut::dependency::RegistrySource;
4146
use crate::util::toml_mut::dependency::Source;
4247
use crate::util::toml_mut::dependency::WorkspaceSource;
4348
use crate::util::toml_mut::manifest::DepTable;
@@ -471,6 +476,13 @@ fn resolve_dependency(
471476
src = src.set_version(v);
472477
}
473478
dependency = dependency.set_source(src);
479+
} else if let Some((registry, public_source)) =
480+
get_public_dependency(spec, manifest, ws, section, gctx, &dependency)?
481+
{
482+
if let Some(registry) = registry {
483+
dependency = dependency.set_registry(registry);
484+
}
485+
dependency = dependency.set_source(public_source);
474486
} else {
475487
let latest =
476488
get_latest_dependency(spec, &dependency, honor_rust_version, gctx, registry)?;
@@ -507,11 +519,119 @@ fn resolve_dependency(
507519
Ok(dependency)
508520
}
509521

522+
fn get_public_dependency(
523+
spec: &Package,
524+
manifest: &LocalManifest,
525+
ws: &Workspace<'_>,
526+
section: &DepTable,
527+
gctx: &GlobalContext,
528+
dependency: &Dependency,
529+
) -> CargoResult<Option<(Option<String>, Source)>> {
530+
if spec
531+
.manifest()
532+
.unstable_features()
533+
.require(Feature::public_dependency())
534+
.is_err()
535+
{
536+
return Ok(None);
537+
}
538+
539+
let (package_set, resolve) = resolve_ws(ws, true)?;
540+
541+
let mut latest: Option<(PackageId, OptVersionReq)> = None;
542+
543+
for (_, path, dep) in manifest.get_dependencies(ws, ws.unstable_features()) {
544+
if path != *section {
545+
continue;
546+
}
547+
548+
let Some(mut dep) = dep.ok() else {
549+
continue;
550+
};
551+
552+
let dep = query_dependency(ws, gctx, &mut dep)?;
553+
let Some(dep_pkgid) = package_set
554+
.package_ids()
555+
.filter(|package_id| {
556+
package_id.name() == dep.package_name()
557+
&& dep.version_req().matches(package_id.version())
558+
})
559+
.max_by_key(|x| x.version())
560+
else {
561+
continue;
562+
};
563+
564+
let mut pkg_ids_and_reqs = Vec::new();
565+
let mut pkg_id_queue = VecDeque::new();
566+
let mut examined = BTreeSet::new();
567+
pkg_id_queue.push_back(dep_pkgid);
568+
569+
while let Some(dep_pkgid) = pkg_id_queue.pop_front() {
570+
let got_deps = resolve.deps(dep_pkgid).filter_map(|(id, deps)| {
571+
deps.iter()
572+
.find(|dep| dep.is_public() && dep.kind() == DepKind::Normal)
573+
.map(|dep| (id, dep))
574+
});
575+
576+
for (pkg_id, got_dep) in got_deps {
577+
if got_dep.package_name() == dependency.name.as_str() {
578+
pkg_ids_and_reqs.push((pkg_id, got_dep.version_req().clone()));
579+
}
580+
581+
if examined.insert(pkg_id.clone()) {
582+
pkg_id_queue.push_back(pkg_id)
583+
}
584+
}
585+
}
586+
587+
for (pkg_id, req) in pkg_ids_and_reqs {
588+
if let Some((old_pkg_id, _)) = &latest
589+
&& old_pkg_id.version() >= pkg_id.version()
590+
{
591+
continue;
592+
}
593+
latest = Some((pkg_id, req))
594+
}
595+
}
596+
597+
let Some((pkg_id, version_req)) = latest else {
598+
return Ok(None);
599+
};
600+
601+
let source = pkg_id.source_id();
602+
if source.is_git() {
603+
Ok(Some((
604+
Option::<String>::None,
605+
Source::Git(GitSource::new(source.as_encoded_url().to_string())),
606+
)))
607+
} else if let Some(path) = source.local_path() {
608+
Ok(Some((None, Source::Path(PathSource::new(path)))))
609+
} else {
610+
let toml_source = match version_req {
611+
crate::util::OptVersionReq::Any => {
612+
Source::Registry(RegistrySource::new(pkg_id.version().to_string()))
613+
}
614+
crate::util::OptVersionReq::Req(version_req)
615+
| crate::util::OptVersionReq::Locked(_, version_req)
616+
| crate::util::OptVersionReq::Precise(_, version_req) => {
617+
Source::Registry(RegistrySource::new(version_req.to_string()))
618+
}
619+
};
620+
Ok(Some((
621+
source
622+
.alt_registry_key()
623+
.map(|x| x.to_owned())
624+
.filter(|_| !source.is_crates_io()),
625+
toml_source,
626+
)))
627+
}
628+
}
629+
510630
fn query_dependency(
511631
ws: &Workspace<'_>,
512632
gctx: &GlobalContext,
513633
dependency: &mut Dependency,
514-
) -> Result<crate::core::Dependency, anyhow::Error> {
634+
) -> CargoResult<crate::core::Dependency> {
515635
let query = dependency.query(gctx)?;
516636
let query = match query {
517637
MaybeWorkspace::Workspace(_workspace) => {

tests/testsuite/cargo_add/public_common_version/out/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ edition = "2015"
88

99
[dependencies]
1010
my-package = "0.1.0"
11-
my-package-dep = "0.2.0"
11+
my-package-dep = "^0.1.0"

tests/testsuite/cargo_add/public_common_version/stderr.term.svg

Lines changed: 11 additions & 5 deletions
Loading

0 commit comments

Comments
 (0)