From 07aff5a3f657e0704d5aa789d0a90a9a3fa30d93 Mon Sep 17 00:00:00 2001 From: tobinio Date: Sun, 28 Dec 2025 11:34:06 +0100 Subject: [PATCH] add `--only_dependency` to prune --- CHANGELOG.md | 3 ++- Cargo.toml | 2 +- src/main.rs | 41 ++++++++++++++++--------------- src/project/dependency/feature.rs | 9 +++++++ src/prune/mod.rs | 14 +++++------ src/prune/parse.rs | 35 +++++++++++++++++++++++++- 6 files changed, 74 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 244c197..87bec2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ## unreleased -* slightly improve handling of workspaces by compiling targets separately +* add `--only_dependency` to prune * move tmpFolder into local Project to avoid conflicts with limited space in /tmp folder +* slightly improve handling of workspaces by compiling targets separately ## 0.10.2 diff --git a/Cargo.toml b/Cargo.toml index ddc53f0..84f1c24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/ToBinio/cargo-features-manager" keywords = ["cli", "manager", "cargo", "crates", "feature"] homepage = "https://github.com/ToBinio/cargo-features-manager.git" readme = "README.md" -rust-version = "1.85.0" +rust-version = "1.86.0" include = ["src/**/*", "LICENSE", "README.md", "Known-Features.toml"] [dependencies] diff --git a/src/main.rs b/src/main.rs index e3f5f9e..1f6e779 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::process::exit; -use clap::{CommandFactory, Parser, Subcommand}; +use clap::{Args, CommandFactory, Parser, Subcommand}; use clap_complete::{Shell, generate}; use color_eyre::Result; use console::Term; @@ -39,18 +39,24 @@ struct FeaturesArgs { #[derive(Subcommand)] enum FeaturesSubCommands { - Prune { - #[arg(long, short)] - dry_run: bool, - #[arg(long, short)] - skip_tests: bool, - /// do not copy the project into a temporary directory - #[arg(long, short = 't')] - no_tmp: bool, - /// `cargo clean` will run after each - #[arg(long, short, default_value_t, value_enum)] - clean: CleanLevel, - }, + Prune(PruneArgs), +} + +#[derive(Args)] +pub struct PruneArgs { + #[arg(long)] + dry_run: bool, + #[arg(long)] + skip_tests: bool, + /// do not copy the project into a temporary directory + #[arg(long, short = 't')] + no_tmp: bool, + /// `cargo clean` will run after each + #[arg(long, short, default_value_t, value_enum)] + clean: CleanLevel, + /// only check features that enable extra dependencies + #[arg(long, short = 'd')] + only_dependency: bool, } #[derive(clap::ValueEnum, Clone, Default, Debug)] @@ -91,13 +97,8 @@ fn run(args: FeaturesArgs) -> Result<()> { if let Some(sub) = args.sub { match sub { - FeaturesSubCommands::Prune { - dry_run, - skip_tests, - clean, - no_tmp, - } => { - prune(dry_run, skip_tests, clean, no_tmp)?; + FeaturesSubCommands::Prune(args) => { + prune(&args)?; } } } else { diff --git a/src/project/dependency/feature.rs b/src/project/dependency/feature.rs index b2cbd97..b368d0c 100644 --- a/src/project/dependency/feature.rs +++ b/src/project/dependency/feature.rs @@ -28,6 +28,15 @@ impl FeatureData { EnabledState::Workspace => false, } } + + pub fn has_dependency_features(&self) -> bool { + self.sub_features.iter().any(|feature| { + matches!( + feature.kind, + SubFeatureType::Dependency | SubFeatureType::DependencyFeature + ) + }) + } } #[derive(Clone, Debug)] diff --git a/src/prune/mod.rs b/src/prune/mod.rs index 4184f0d..a4a6dcd 100644 --- a/src/prune/mod.rs +++ b/src/prune/mod.rs @@ -1,9 +1,9 @@ -use crate::CleanLevel; use crate::io::save::save_dependency; use crate::project::dependency::Dependency; use crate::project::document::Document; use crate::prune::display::Display; use crate::prune::parse::get_features_to_test; +use crate::{CleanLevel, PruneArgs}; use color_eyre::Result; use color_eyre::eyre::{ContextCompat, eyre}; use dircpy::copy_dir; @@ -24,13 +24,13 @@ pub type DependencyName = String; pub type FeatureName = String; pub type FeaturesMap = HashMap>>; -pub fn prune(is_dry_run: bool, skip_tests: bool, clean: CleanLevel, no_tmp: bool) -> Result<()> { +pub fn prune(args: &PruneArgs) -> Result<()> { let mut main_document = Document::new(".")?; //needed to be set here so the temp_dir lives long enough let tmp_dir = TempDir::with_prefix_in(".cargo-features-manager-", ".")?; - let mut document = if no_tmp { + let mut document = if args.no_tmp { Document::new(".")? } else { println!("Creating temporary project..."); @@ -46,17 +46,17 @@ pub fn prune(is_dry_run: bool, skip_tests: bool, clean: CleanLevel, no_tmp: bool } }; - let features_to_test = get_features_to_test(&document)?; + let features_to_test = get_features_to_test(&document, args.only_dependency)?; let mut pruner = Pruner { - skip_tests, - clean_level: clean, + skip_tests: args.skip_tests, + clean_level: args.clean.clone(), document: &mut document, known_features: known_features()?, }; let to_be_disabled = pruner.run(features_to_test)?; - if is_dry_run { + if args.dry_run { return Ok(()); } diff --git a/src/prune/parse.rs b/src/prune/parse.rs index f088589..fbd4a9b 100644 --- a/src/prune/parse.rs +++ b/src/prune/parse.rs @@ -9,11 +9,19 @@ use std::collections::HashMap; use std::ops::Not; use std::path::Path; -pub fn get_features_to_test(document: &Document) -> Result { +pub fn get_features_to_test( + document: &Document, + only_dependency_features: bool, +) -> Result { let base_ignored_features = get_ignored_features("./", "workspace.cargo-features-manager.keep")?; let mut enabled_features = get_enabled_features(document); + + if only_dependency_features { + remove_non_dependency_features(document, &mut enabled_features)?; + } + remove_ignored_features(document, &base_ignored_features, &mut enabled_features)?; Ok(enabled_features) @@ -88,6 +96,31 @@ fn get_ignored_features>( } } +fn remove_non_dependency_features( + document: &Document, + enabled_features: &mut FeaturesMap, +) -> Result<()> { + for (package_name, dependencies) in enabled_features { + let package = document.get_package(package_name)?; + + for (dependency_name, features) in dependencies { + let dependency = package.get_dep(dependency_name)?; + + for feature_name in &features.clone() { + let Some(feature) = dependency.get_feature(feature_name) else { + continue; + }; + + if feature.has_dependency_features().not() { + remove_feature(feature_name, features, dependency); + } + } + } + } + + Ok(()) +} + fn remove_ignored_features( document: &Document, base_ignored: &HashMap>, -- 2.51.2