diff --git a/src/io/save.rs b/src/io/save.rs index a3a40b2..2c6e60d 100644 --- a/src/io/save.rs +++ b/src/io/save.rs @@ -9,7 +9,7 @@ pub fn save_dependency( package_name: &str, dep_name: &str, ) -> color_eyre::Result<()> { - let package = document.get_package_mut(package_name)?; + let package = document.get_package(package_name)?; let dependency = package.get_dep(dep_name)?; let features_to_enable = dependency.get_features_to_enable(); diff --git a/src/io/util.rs b/src/io/util.rs index 0bb5e8c..bfc7d6d 100644 --- a/src/io/util.rs +++ b/src/io/util.rs @@ -142,6 +142,7 @@ fn get_dependency_path(kind: &DependencyType, target: &Option) -> Stri #[cfg(test)] mod test { + #![allow(clippy::unwrap_used)] use cargo_metadata::cargo_platform::{Cfg, CfgExpr, Ident, Platform}; use std::io::Write; use tempfile::NamedTempFile; diff --git a/src/main.rs b/src/main.rs index 53f60f7..e3f5f9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::process::exit; -use clap::{CommandFactory, Parser, Subcommand, arg}; +use clap::{CommandFactory, Parser, Subcommand}; use clap_complete::{Shell, generate}; use color_eyre::Result; use console::Term; diff --git a/src/project/dependency/feature.rs b/src/project/dependency/feature.rs index 9c8f6f0..b2cbd97 100644 --- a/src/project/dependency/feature.rs +++ b/src/project/dependency/feature.rs @@ -36,10 +36,13 @@ pub struct SubFeature { pub kind: SubFeatureType, } +/// https://doc.rust-lang.org/cargo/reference/features.html #[derive(Clone, PartialEq, Debug)] pub enum SubFeatureType { Normal, + /// e.g. dep:gif Dependency, + /// e.g. jpeg-decoder/rayon DependencyFeature, } diff --git a/src/prune/mod.rs b/src/prune/mod.rs index 74c00f8..108a007 100644 --- a/src/prune/mod.rs +++ b/src/prune/mod.rs @@ -12,8 +12,6 @@ use std::collections::HashMap; use std::ops::Not; use std::path::Path; use std::process::{Command, Stdio}; -use std::thread; -use std::time::Duration; use tempfile::TempDir; mod parse; @@ -52,13 +50,14 @@ 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 to_be_disabled = prune_features( - &mut document, + + let mut pruner = Pruner { skip_tests, - clean, - features_to_test, - known_features()?, - )?; + clean_level: clean, + document: &mut document, + known_features: known_features()?, + }; + let to_be_disabled = pruner.run(features_to_test)?; if is_dry_run { return Ok(()); @@ -105,131 +104,181 @@ pub fn known_features() -> Result>> { Ok(map) } -fn prune_features( - document: &mut Document, +struct Pruner<'a> { skip_tests: bool, - should_clean: CleanLevel, - features: FeaturesMap, + clean_level: CleanLevel, + document: &'a mut Document, known_features: HashMap>, -) -> Result { - let mut features_map = HashMap::new(); +} + +impl<'a> Pruner<'a> { + fn run(&mut self, all_features: FeaturesMap) -> Result { + let mut features_map = HashMap::new(); + + let mut has_known_features_enabled = false; + + let mut display = Display::new(&all_features, self.document); + display.start()?; + + for (package_name, dependencies) in all_features + .into_iter() + .sorted_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)) + { + if self.prune_package( + &package_name, + &dependencies, + &mut display, + &mut features_map, + )? { + has_known_features_enabled = true + }; + } - let mut has_known_features_enabled = false; + if has_known_features_enabled { + display.display_known_features_notice()?; + } + + display.finish()?; - let mut display = Display::new(&features, document); - display.start()?; + Ok(features_map) + } - for (package_name, dependencies) in features - .into_iter() - .sorted_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)) - { + fn prune_package( + &mut self, + package_name: &str, + dependencies: &HashMap>, + display: &mut Display, + features_map: &mut FeaturesMap, + ) -> Result { if dependencies.is_empty() { - continue; + return Ok(false); } - display.next_package(&package_name, &dependencies)?; + let mut has_known_features_enabled = false; + + display.next_package(package_name, dependencies)?; for (dependency_name, features) in dependencies - .into_iter() + .iter() .sorted_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)) { - if features.is_empty() { - continue; - } + if self.prune_dependency( + package_name, + dependency_name, + features, + display, + features_map, + )? { + has_known_features_enabled = true; + }; + } - let mut known_features_list = vec![]; - let dependency = document - .get_package(&package_name)? - .get_dep(&dependency_name)?; - - for feature_name in known_features.get(&dependency_name).unwrap_or(&vec![]) { - set_features_to_be_kept( - dependency, - feature_name.to_string(), - &mut known_features_list, - ) - } + if let CleanLevel::Package = self.clean_level { + clean(self.document.root_path())?; + } - let mut to_be_disabled = vec![]; - to_be_disabled.append(&mut known_features_list.clone()); + Ok(has_known_features_enabled) + } - display.next_dependency(&dependency_name, &features); + fn prune_dependency( + &mut self, + package_name: &str, + dependency_name: &str, + features: &Vec, + display: &mut Display, + features_map: &mut FeaturesMap, + ) -> Result { + if features.is_empty() { + return Ok(false); + } - for (id, feature) in features.iter().enumerate() { - display.next_feature(id, feature)?; + let mut has_known_features_enabled = false; - document - .get_package_mut(&package_name)? - .get_dep_mut(&dependency_name)? - .disable_feature(feature)?; - - save_dependency(document, &package_name, &dependency_name)?; - - if !to_be_disabled.contains(feature) && check(skip_tests, document.root_path())? { - set_features_to_be_disabled( - document - .get_package(&package_name)? - .get_dep(&dependency_name)?, - feature.to_string(), - &mut to_be_disabled, - ); - } + let mut known_features_list = vec![]; + let dependency = self + .document + .get_package(package_name)? + .get_dep(dependency_name)?; - //reset to start - for feature in &features { - document - .get_package_mut(&package_name)? - .get_dep_mut(&dependency_name)? - .enable_feature(feature)?; - } + for feature_name in self.known_features.get(dependency_name).unwrap_or(&vec![]) { + set_features_to_be_kept( + dependency, + feature_name.to_string(), + &mut known_features_list, + ) + } - save_dependency(document, &package_name, &dependency_name)?; + let mut to_be_disabled = vec![]; + to_be_disabled.append(&mut known_features_list.clone()); - display.finish_feature()?; + display.next_dependency(dependency_name, features); + + for (id, feature) in features.iter().enumerate() { + display.next_feature(id, feature)?; + + self.document + .get_package_mut(package_name)? + .get_dep_mut(dependency_name)? + .disable_feature(feature)?; + + save_dependency(self.document, package_name, dependency_name)?; + + if to_be_disabled.contains(feature).not() + && check(self.skip_tests, self.document.root_path())? + { + set_features_to_be_disabled( + self.document + .get_package(package_name)? + .get_dep(dependency_name)?, + feature.to_string(), + &mut to_be_disabled, + ); } - let features_result = features - .iter() - .filter(|feature| to_be_disabled.contains(feature)) - .map(|feature| { - if known_features_list.contains(feature) { - has_known_features_enabled = true; - (feature, true) - } else { - (feature, false) - } - }) - .collect(); - - display.finish_dependency(features_result)?; - - if let CleanLevel::Dependency = should_clean { - clean(document.root_path())?; + //reset to start + for feature in features { + self.document + .get_package_mut(package_name)? + .get_dep_mut(dependency_name)? + .enable_feature(feature)?; } - let to_be_disabled = to_be_disabled - .into_iter() - .filter(|feature| known_features_list.contains(feature).not()) - .collect_vec(); + save_dependency(self.document, package_name, dependency_name)?; - features_map - .entry(package_name.to_string()) - .or_insert_with(HashMap::new) - .insert(dependency_name, to_be_disabled); + display.finish_feature()?; } - if let CleanLevel::Package = should_clean { - clean(document.root_path())?; + let features_result = features + .iter() + .filter(|feature| to_be_disabled.contains(feature)) + .map(|feature| { + if known_features_list.contains(feature) { + has_known_features_enabled = true; + (feature, true) + } else { + (feature, false) + } + }) + .collect(); + + display.finish_dependency(features_result)?; + + if let CleanLevel::Dependency = self.clean_level { + clean(self.document.root_path())?; } - } - if has_known_features_enabled { - display.display_known_features_notice()?; - } + let to_be_disabled = to_be_disabled + .into_iter() + .filter(|feature| known_features_list.contains(feature).not()) + .collect_vec(); - display.finish()?; + features_map + .entry(package_name.to_string()) + .or_default() + .insert(dependency_name.to_string(), to_be_disabled); - Ok(features_map) + Ok(has_known_features_enabled) + } } fn set_features_to_be_disabled(