diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e3b8f..dc617e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ## unreleased + +* #### BREAKING - move Features.toml into Cargo.toml see [README.md](README.md#prune) + * use `color-eyre` instead of `anyhow` * handle unused workspace dependencies * allow `default` to be a sub_feature diff --git a/Cargo.toml b/Cargo.toml index 4a0b780..c76084a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,7 @@ toml_edit = { version = "0.22.12", default-features = false, features = ["displa [[bin]] name = "cargo-features" path = "src/main.rs" + +[cargo-features-manager.keep] +clap = ["default"] +color-eyre = ["default"] diff --git a/Features.toml b/Features.toml deleted file mode 100644 index 692ad98..0000000 --- a/Features.toml +++ /dev/null @@ -1,2 +0,0 @@ -clap = ["default"] -color-eyre = ["default"] \ No newline at end of file diff --git a/README.md b/README.md index 03d46fe..5967482 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,15 @@ A TUI-like cli tool to manage the features of your rust-projects dependencies. You can view all available features and easily toggle (enable & disable) them with one button click. All of your changes will directly be reflected in your Cargo.toml file. +--- + ## install `cargo install cargo-features-manager` -## usage +--- -### viewer +## usage To start the tool run `cargo features` in your project root dir. @@ -27,7 +29,7 @@ Selecting a dependency will open the feature-selector: When using `cargo features -d ` it will directly open the corresponding feature-selector. -#### navigation +### navigation โ†‘ to move up @@ -37,7 +39,7 @@ When using `cargo features -d ` it will directly open the corre ESC | โ† to move back -#### dependency selector +### dependency selector Dependency which do not have any features are marked grey.
Dev-Dependency are marked with ๐Ÿงช.
@@ -46,7 +48,7 @@ Workspace-Dependency are marked with ๐Ÿ—ƒ๏ธ๏ธ. ![dependencySelector](resources/dependencySelector.png) -#### feature selector +### feature selector All default features are marked Green. @@ -68,22 +70,32 @@ Features marked with ๐Ÿ—ƒ๏ธ๏ธ are enabled by the workspace dependency and can ![workspaceFeatures](resources/workspaceFeatures.png) -#### search mode +### search mode At any point you can start typing like normal. This will start using your input as a search query. -### prune +--- + +## prune You can run prune with `cargo features prune` this will disable all features which are not required to compile. -#### always keep +### always keep -If your project requires a features to be enabled which does not make the compile fail. you can create a file -called `Features.toml` in there you can define features which will not get disabled. +If your project requires features to be enabled which do not make the compile fail. You can add a section to +your `Cargo.toml` named `cargo-features-manager.keep` in there you can define which features will be kept. ```toml -clap = ["help"] +# for individial packages +[cargo-features-manager.keep] +clap = ["default"] +color-eyre = ["capture-spantrace", "track-caller"] + +# for the whole workspace +[workspace.cargo-features-manager.keep] +clap = ["default"] +color-eyre = ["capture-spantrace", "track-caller"] ``` \ No newline at end of file diff --git a/src/parsing/dependency.rs b/src/parsing/dependency.rs index 07a0783..f099941 100644 --- a/src/parsing/dependency.rs +++ b/src/parsing/dependency.rs @@ -1,7 +1,8 @@ use crate::parsing::package::get_package_from_version; use crate::project::dependency::feature::{EnabledState, FeatureData, SubFeature, SubFeatureType}; -use crate::project::dependency::util::{get_item_from_doc, get_path}; +use crate::project::dependency::util::get_path; use crate::project::dependency::{Dependency, DependencyType}; +use crate::util::get_item_from_doc; use cargo_metadata::PackageId; use color_eyre::eyre::{eyre, ContextCompat}; use itertools::Itertools; diff --git a/src/project/dependency/util.rs b/src/project/dependency/util.rs index 047f809..558e95c 100644 --- a/src/project/dependency/util.rs +++ b/src/project/dependency/util.rs @@ -1,7 +1,5 @@ use crate::project::dependency::DependencyType; use cargo_platform::Platform; -use color_eyre::eyre::{bail, eyre, ContextCompat}; -use std::str::FromStr; pub fn get_path(kind: &DependencyType, target: &Option) -> String { let path = match kind { @@ -21,87 +19,3 @@ pub fn get_path(kind: &DependencyType, target: &Option) -> String { path.to_string() } - -pub fn get_mut_item_from_doc<'a>( - path: &str, - document: &'a mut toml_edit::DocumentMut, -) -> color_eyre::Result<&'a mut toml_edit::Item> { - let mut item = document.as_item_mut(); - - let mut is_target = false; - - 'outer: for key in path.split('.') { - if is_target { - is_target = false; - - let target = Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; - - let table = item - .as_table_like_mut() - .context(eyre!("could not find - {} - no table", path))?; - - for (key, next_item) in table.iter_mut() { - let platform = - Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; - - if platform.eq(&target) { - item = next_item; - continue 'outer; - } - } - - bail!("could not find - {} - no table", path) - } - - item = item - .get_mut(key) - .context(eyre!("could not find - {}", path))?; - - if key == "target" { - is_target = true; - } - } - - Ok(item) -} - -pub fn get_item_from_doc<'a>( - path: &str, - document: &'a toml_edit::DocumentMut, -) -> color_eyre::Result<&'a toml_edit::Item> { - let mut item = document.as_item(); - - let mut is_target = false; - - 'outer: for key in path.split('.') { - if is_target { - is_target = false; - - let target = Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; - - let table = item - .as_table() - .context(eyre!("could not find - {} - no table", path))?; - - for (key, next_item) in table.iter() { - let platform = - Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; - - if platform.eq(&target) { - item = next_item; - continue 'outer; - } - } - - bail!("could not find - {} - no table", path) - } - - item = item.get(key).context(eyre!("could not find - {}", path))?; - - if key == "target" { - is_target = true; - } - } - - Ok(item) -} diff --git a/src/project/document.rs b/src/project/document.rs index f6b8e7e..fafd155 100644 --- a/src/project/document.rs +++ b/src/project/document.rs @@ -102,13 +102,6 @@ impl Document { &self.packages } - pub fn get_packages_names(&self) -> Vec { - self.packages - .iter() - .map(|package| package.name.to_string()) - .collect() - } - pub fn get_package_by_id(&self, package_id: usize) -> Result<&Package> { self.packages .get(package_id) diff --git a/src/prune.rs b/src/prune.rs index c27bfaf..1b34aed 100644 --- a/src/prune.rs +++ b/src/prune.rs @@ -10,18 +10,17 @@ use std::path::Path; use crate::project::dependency::Dependency; use crate::project::document::Document; use crate::save::save_dependency; -use color_eyre::eyre::eyre; +use crate::util::{get_item_from_doc, toml_document_from_path}; +use color_eyre::eyre::{eyre, ContextCompat}; use std::process::{Command, Stdio}; -use toml::Table; - -//todo test with renamed dependencies!!! pub fn prune(mut document: Document, is_dry_run: bool) -> Result<()> { let mut term = Term::stdout(); let mut enabled_features = get_enabled_features(&document); - let base_ignored_features = get_ignored_features("./")?; + let base_ignored_features = + get_ignored_features("./", "workspace.cargo-features-manager.keep")?; remove_ignored_features(&document, &base_ignored_features, &mut enabled_features)?; prune_features(&mut document, is_dry_run, &mut term, enabled_features)?; @@ -45,7 +44,7 @@ fn get_enabled_features(document: &Document) -> HashMap>(); if enabled_features.is_empty().not() { - package_data.insert(dependency.name.clone(), enabled_features); + package_data.insert(dependency.get_name().clone(), enabled_features); } } @@ -53,6 +52,7 @@ fn get_enabled_features(document: &Document) -> HashMap Result { Ok(code == 0) } -fn get_ignored_features>(base_path: P) -> Result>> { - let result = fs::read_to_string(base_path.as_ref().join("Features.toml")); +fn get_ignored_features>( + file_path: P, + item_path: &str, +) -> Result>> { + let result = toml_document_from_path(file_path.as_ref().join("Cargo.toml")); match result { - Ok(file) => { - let table = file.parse::()?; + Ok(document) => { + let item = get_item_from_doc(&item_path, &document); + + let Ok(item) = item else { + return Ok(HashMap::new()); + }; + + let table = item.as_table_like().context(format!( + "could not parse {} in {:?}", + item_path, + file_path.as_ref() + ))?; let mut map = HashMap::new(); - for (key, value) in table { + for (key, value) in table.iter() { map.insert( - key, + key.to_string(), value .as_array() - .ok_or(eyre!("Invalid Features.toml format"))? + .ok_or(eyre!("Invalid format to keep features"))? .iter() - .to_owned() .filter_map(|value| value.as_str()) .map(|value| value.to_string()) .collect(), ); } + Ok(map) } Err(_) => Ok(HashMap::new()), diff --git a/src/save.rs b/src/save.rs index 43fccff..5fd2d4b 100644 --- a/src/save.rs +++ b/src/save.rs @@ -1,6 +1,6 @@ -use crate::project::dependency::util::{get_mut_item_from_doc, get_path}; +use crate::project::dependency::util::get_path; use crate::project::document::Document; -use crate::util::toml_document_from_path; +use crate::util::{get_mut_item_from_doc, toml_document_from_path}; use color_eyre::eyre::{ContextCompat, Error}; use std::fs; use toml_edit::{Array, Formatted, InlineTable, Item, Value}; diff --git a/src/util.rs b/src/util.rs index cfce633..5031d7a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,8 @@ -use color_eyre::eyre::eyre; +use color_eyre::eyre::{bail, ContextCompat, eyre}; use std::fs; use std::path::Path; +use std::str::FromStr; +use cargo_platform::Platform; pub fn toml_document_from_path>( dir_path: P, @@ -10,3 +12,88 @@ pub fn toml_document_from_path>( Ok(file_content.parse()?) } + + +pub fn get_mut_item_from_doc<'a>( + path: &str, + document: &'a mut toml_edit::DocumentMut, +) -> color_eyre::Result<&'a mut toml_edit::Item> { + let mut item = document.as_item_mut(); + + let mut is_target = false; + + 'outer: for key in path.split('.') { + if is_target { + is_target = false; + + let target = Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; + + let table = item + .as_table_like_mut() + .context(eyre!("could not find - {} - no table", path))?; + + for (key, next_item) in table.iter_mut() { + let platform = + Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; + + if platform.eq(&target) { + item = next_item; + continue 'outer; + } + } + + bail!("could not find - {} - no table", path) + } + + item = item + .get_mut(key) + .context(eyre!("could not find - {}", path))?; + + if key == "target" { + is_target = true; + } + } + + Ok(item) +} + +pub fn get_item_from_doc<'a>( + path: &str, + document: &'a toml_edit::DocumentMut, +) -> color_eyre::Result<&'a toml_edit::Item> { + let mut item = document.as_item(); + + let mut is_target = false; + + 'outer: for key in path.split('.') { + if is_target { + is_target = false; + + let target = Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; + + let table = item + .as_table() + .context(eyre!("could not find - {} - no table", path))?; + + for (key, next_item) in table.iter() { + let platform = + Platform::from_str(key.trim_start_matches('\'').trim_end_matches('\''))?; + + if platform.eq(&target) { + item = next_item; + continue 'outer; + } + } + + bail!("could not find - {} - no table", path) + } + + item = item.get(key).context(eyre!("could not find - {}", path))?; + + if key == "target" { + is_target = true; + } + } + + Ok(item) +} \ No newline at end of file