Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion src/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use anyhow::{Context, Result};
use cargo::{
core::{dependency::DepKind, resolver::CliFeatures, FeatureValue, Package, Workspace},
ops::{Packages, PublishOpts},
sources::IndexSummary,
util::{cache_lock::CacheLockMode, toml_mut::manifest::LocalManifest},
};

use semver::Version;
use semver::{Version, VersionReq};

use std::{
collections::{BTreeMap, BTreeSet},
Expand Down Expand Up @@ -49,6 +50,10 @@ pub async fn handle_apply(args: Args, apply: Apply) -> Result<()> {
let mut plan: Planner = toml::from_str(&plan)?;
expand_plan(&workspace, &workspace_crates, &mut plan, &upstream).await?;

let local_path_overrides = apply
.registry
.then(|| compute_must_use_local(&workspace, &plan, &upstream));

if apply.print {
list(&path, &cargo_config, &plan)?;
return Ok(());
Expand Down Expand Up @@ -96,6 +101,7 @@ pub async fn handle_apply(args: Args, apply: Apply) -> Result<()> {
&upstream,
&pkg.rewrite_dep,
apply.registry,
local_path_overrides.as_ref(),
)?;

for remove_feature in &pkg.remove_feature {
Expand Down Expand Up @@ -272,3 +278,61 @@ fn remove_dev_features(member: &Package) -> Vec<RemoveFeature> {

remove
}

/// Compute the transitive closure of crates that must use local paths.
///
/// A crate must use a local path if:
/// 1. Its new version doesn't exist on crates.io, OR
/// 2. It depends (transitively) on a crate that must use a local path
fn compute_must_use_local(
workspace: &Workspace,
plan: &Planner,
upstream: &BTreeMap<String, Vec<IndexSummary>>,
) -> BTreeSet<String> {
// Step 1: Find crates whose new version doesn't exist on crates.io
let mut must_use_local = BTreeSet::new();
for pkg in &plan.crates {
let ver = VersionReq::parse(&pkg.to).ok();
let has_upstream = ver.as_ref().and_then(|v| {
upstream.get(&pkg.name).and_then(|versions| {
versions
.iter()
.find(|u| v.matches(u.as_summary().version()))
})
});

if has_upstream.is_none() {
must_use_local.insert(pkg.name.clone());
}
}

// Step 2: Build reverse dependency graph (crate → dependents)
let mut dependents: BTreeMap<String, Vec<String>> = BTreeMap::new();
for member in workspace.members() {
let name = member.name().to_string();
for dep in member
.dependencies()
.iter()
.filter(|d| d.kind() != DepKind::Development)
{
dependents
.entry(dep.package_name().to_string())
.or_default()
.push(name.clone());
}
}

// Step 3: BFS to propagate to all transitive dependents
let mut queue: Vec<String> = must_use_local.iter().cloned().collect();
while let Some(crate_name) = queue.pop() {
if let Some(deps) = dependents.get(&crate_name) {
for dep in deps {
if must_use_local.insert(dep.clone()) {
queue.push(dep.clone());
}
}
}
}

must_use_local
}
8 changes: 7 additions & 1 deletion src/edit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::read_to_string;
use std::path::Path;

Expand All @@ -23,6 +23,7 @@ pub fn rewrite_workspace_dep(
cdep: &mut Dependency,
dev: bool,
use_registry: bool,
local_path_overrides: Option<&BTreeSet<String>>,
) -> Result<()> {
let wdeps = root_manifest
.get_mut("workspace")
Expand Down Expand Up @@ -72,6 +73,7 @@ pub fn rewrite_workspace_dep(
if let Some(pkg) = workspace_crates.get(name) {
if pkg.publish().is_none()
&& use_registry
&& !local_path_overrides.is_some_and(|s| s.contains(name))
&& upstream
.get(name)
.and_then(|d| d.iter().find(|d| ver.matches(d.as_summary().version())))
Expand Down Expand Up @@ -102,6 +104,7 @@ pub fn rewrite_deps(
upstream: &BTreeMap<String, Vec<IndexSummary>>,
deps: &[RewriteDep],
use_registry: bool,
local_path_overrides: Option<&BTreeSet<String>>,
) -> Result<()> {
for dep in deps {
let exisiting_deps = manifest
Expand Down Expand Up @@ -134,6 +137,7 @@ pub fn rewrite_deps(
&mut existing_dep,
dev,
use_registry,
local_path_overrides,
)?;
manifest.insert_into_table(
&table,
Expand Down Expand Up @@ -163,6 +167,8 @@ pub fn rewrite_deps(
let ver = VersionReq::parse(&new_ver).unwrap();
if pkg.publish().is_none()
&& use_registry
&& !local_path_overrides
.is_some_and(|s| s.contains(existing_dep.name.as_str()))
&& upstream
.get(existing_dep.name.as_str())
.and_then(|d| d.iter().find(|d| ver.matches(d.as_summary().version())))
Expand Down