use rustc_hash::FxHashSet;
use toml_edit::{DocumentMut, Item, Table};
pub struct CargoTomlEditor;
impl CargoTomlEditor {
pub fn remove_dependencies(
manifest: &mut DocumentMut,
unused_deps: &FxHashSet<String>,
) -> usize {
if unused_deps.is_empty() {
return 0;
}
Self::remove_workspace_dependencies(manifest, unused_deps);
Self::remove_package_dependencies(manifest, unused_deps);
Self::remove_target_dependencies(manifest, unused_deps);
Self::fix_features(manifest, unused_deps);
unused_deps.len()
}
pub fn move_to_dev_dependencies(
manifest: &mut DocumentMut,
misplaced_deps: &FxHashSet<String>,
) -> usize {
if misplaced_deps.is_empty() {
return 0;
}
let mut moved = Vec::new();
if let Some(dependencies) =
manifest.get_mut("dependencies").and_then(|item| item.as_table_mut())
{
for dep in misplaced_deps {
if let Some(value) = dependencies.remove(dep.as_str()) {
moved.push((dep.clone(), value));
}
}
}
if moved.is_empty() {
return 0;
}
let count = moved.len();
if !manifest.contains_key("dev-dependencies") {
manifest["dev-dependencies"] = Item::Table(Table::new());
}
if let Some(dev_deps) =
manifest.get_mut("dev-dependencies").and_then(|item| item.as_table_mut())
{
for (dep, value) in moved {
dev_deps.insert(&dep, value);
}
}
count
}
fn remove_workspace_dependencies(manifest: &mut DocumentMut, unused_deps: &FxHashSet<String>) {
if let Some(dependencies) = manifest
.get_mut("workspace")
.and_then(|item| item.as_table_mut())
.and_then(|table| table.get_mut("dependencies"))
.and_then(|item| item.as_table_mut())
{
dependencies.retain(|k, _| !unused_deps.contains(k));
}
}
fn remove_package_dependencies(manifest: &mut DocumentMut, unused_deps: &FxHashSet<String>) {
for table_name in ["dependencies", "dev-dependencies", "build-dependencies"] {
if let Some(dependencies) =
manifest.get_mut(table_name).and_then(|item| item.as_table_mut())
{
dependencies.retain(|k, _| !unused_deps.contains(k));
}
}
}
fn remove_target_dependencies(manifest: &mut DocumentMut, unused_deps: &FxHashSet<String>) {
let Some(target) = manifest.get_mut("target").and_then(|item| item.as_table_mut()) else {
return;
};
for (_, item) in target.iter_mut() {
let Some(table) = item.as_table_mut() else {
continue;
};
for name in ["dependencies", "dev-dependencies", "build-dependencies"] {
if let Some(dependencies) = table.get_mut(name).and_then(|item| item.as_table_mut())
{
dependencies.retain(|k, _| !unused_deps.contains(k));
}
}
}
}
fn fix_features(manifest: &mut DocumentMut, unused_deps: &FxHashSet<String>) {
if let Some(features) = manifest.get_mut("features").and_then(|item| item.as_table_mut()) {
for (_feature_name, dependencies) in features.iter_mut() {
if let Some(dependencies) = dependencies.as_array_mut() {
dependencies.retain(|dep| {
dep.as_str().is_none_or(|dep_str| {
dep_str.strip_prefix("dep:").map_or_else(
|| !unused_deps.contains(dep_str),
|dep_name| !unused_deps.contains(dep_name),
)
})
});
}
}
}
}
}