├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── cleaner.rs ├── cleaner ├── cargo.rs ├── git.rs ├── gradle.rs ├── maven.rs ├── mix.rs └── node.rs ├── main.rs └── options.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | name: Rust (${{ matrix.rust }}) (${{ matrix.os }}) 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - macos-latest 18 | - ubuntu-latest 19 | - windows-latest 20 | rust: 21 | - stable 22 | - beta 23 | - nightly 24 | 25 | steps: 26 | - uses: actions/checkout@v2 27 | 28 | - uses: actions-rs/toolchain@v1 29 | with: 30 | profile: minimal 31 | toolchain: ${{ matrix.rust }} 32 | override: true 33 | components: rustfmt, clippy 34 | 35 | - uses: actions-rs/cargo@v1 36 | with: 37 | command: build 38 | 39 | - uses: actions-rs/cargo@v1 40 | with: 41 | command: test 42 | 43 | - uses: actions-rs/cargo@v1 44 | with: 45 | command: fmt 46 | args: --all -- --check 47 | 48 | - uses: actions-rs/cargo@v1 49 | with: 50 | command: clippy 51 | args: --all --all-features --profile test 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /**/*.rs.bk 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.11.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.14" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "bitflags" 23 | version = "1.2.1" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "clap" 28 | version = "2.33.3" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", 33 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "detox" 42 | version = "0.1.2" 43 | dependencies = [ 44 | "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", 45 | "walkdir 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "hermit-abi" 50 | version = "0.1.19" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | dependencies = [ 53 | "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)", 54 | ] 55 | 56 | [[package]] 57 | name = "libc" 58 | version = "0.2.98" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | 61 | [[package]] 62 | name = "same-file" 63 | version = "1.0.6" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | dependencies = [ 66 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 67 | ] 68 | 69 | [[package]] 70 | name = "strsim" 71 | version = "0.8.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | 74 | [[package]] 75 | name = "textwrap" 76 | version = "0.11.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | dependencies = [ 79 | "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 80 | ] 81 | 82 | [[package]] 83 | name = "unicode-width" 84 | version = "0.1.8" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | 87 | [[package]] 88 | name = "vec_map" 89 | version = "0.8.2" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | 92 | [[package]] 93 | name = "walkdir" 94 | version = "2.3.2" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | dependencies = [ 97 | "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 100 | ] 101 | 102 | [[package]] 103 | name = "winapi" 104 | version = "0.3.9" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | dependencies = [ 107 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 108 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 109 | ] 110 | 111 | [[package]] 112 | name = "winapi-i686-pc-windows-gnu" 113 | version = "0.4.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | 116 | [[package]] 117 | name = "winapi-util" 118 | version = "0.1.5" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | dependencies = [ 121 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 122 | ] 123 | 124 | [[package]] 125 | name = "winapi-x86_64-pc-windows-gnu" 126 | version = "0.4.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | 129 | [metadata] 130 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 131 | "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 132 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 133 | "checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 134 | "checksum hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 135 | "checksum libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)" = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" 136 | "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 137 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 138 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 139 | "checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 140 | "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 141 | "checksum walkdir 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 142 | "checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 143 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 144 | "checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 145 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 146 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "detox" 3 | version = "0.1.2" # remember to update html_root_url 4 | authors = ["Isaac Whitfield "] 5 | description = "Quickly clean up your development directories on disk" 6 | repository = "https://github.com/whitfin/detox" 7 | keywords = ["backup", "compression", "clean"] 8 | categories = ["algorithms", "command-line-utilities", "filesystem"] 9 | readme = "README.md" 10 | edition = "2018" 11 | license = "MIT" 12 | 13 | [dependencies] 14 | clap = "2.33" 15 | walkdir = "2.2" 16 | 17 | [profile.release] 18 | codegen-units = 1 19 | opt-level = 3 20 | lto = true 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Isaac Whitfield 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Detox 2 | [![Crates.io](https://img.shields.io/crates/v/detox.svg)](https://crates.io/crates/detox) 3 | [![Build Status](https://img.shields.io/github/workflow/status/whitfin/detox/CI)](https://github.com/whitfin/detox/actions) 4 | 5 | Detox is a very small CLI tool used to clean up development directories to 6 | save disk space. This is particularly handy to use prior to backing up your 7 | development machine. It was written as a personal tool, but figured it might 8 | be useful to others. 9 | 10 | The main aim is to reduce the amount of space in project directories without 11 | specifically altering the projects. This pretty much means the basics, such 12 | as removing target directories, dependency directories, compressing version 13 | control trees, etc. 14 | 15 | The list of structures supported is as below. If you want to add support for 16 | a new structure, feel free; the initial list is pretty much based on what I 17 | have on my development machines on a daily basis. 18 | 19 | * Cargo (Rust) 20 | * Git 21 | * Gradle (Java) 22 | * Maven (Java) 23 | * Mix (Elixir) 24 | * Node.js 25 | 26 | If this is the first time you have run this tool, please do read the source 27 | to validate that it's not going to wipe something important. It should be 28 | fairly safe given that it only looks at build files etc, but know that you 29 | are running it at your own risk! 30 | 31 | ## Installation 32 | 33 | Detox is written in Rust, and is available for download via the repository 34 | on [crates.io](https://crates.io/crates/detox). The easiest way to get this 35 | at this point is to install it via Cargo: 36 | 37 | ```shell 38 | $ cargo install detox 39 | ``` 40 | 41 | ## Usage 42 | 43 | The CLI is tiny and instructions are provided via the documentation: 44 | 45 | ```shell 46 | $ detox -h 47 | $ detox 48 | ``` 49 | 50 | Locations are checked recursively for files which might signal a development 51 | directory. Various files which are "unnecessary" will then be stripped away, 52 | and the output will tell you how much space you saved. 53 | -------------------------------------------------------------------------------- /src/cleaner.rs: -------------------------------------------------------------------------------- 1 | //! Cleaning traits and implementations. 2 | mod cargo; 3 | mod git; 4 | mod gradle; 5 | mod maven; 6 | mod mix; 7 | mod node; 8 | 9 | pub use cargo::CargoCleaner; 10 | pub use git::GitCleaner; 11 | pub use gradle::GradleCleaner; 12 | pub use maven::MavenCleaner; 13 | pub use mix::MixCleaner; 14 | pub use node::NodeCleaner; 15 | 16 | use std::fs; 17 | use std::io::{self, ErrorKind}; 18 | use std::process::{Command, Stdio}; 19 | 20 | /// Trait to represent a cleaning structure. 21 | pub trait Cleaner { 22 | /// Returns the name of the current cleaner. 23 | fn name(&self) -> &str; 24 | 25 | /// Cleans a directory assumed to be a relevant directory. 26 | fn clean(&self, dir: &str) -> io::Result<()>; 27 | 28 | /// Returns a set of file names which identify a relevant directory. 29 | fn triggers(&self) -> &[&str]; 30 | } 31 | 32 | /// Executes a command in a directory using provided arguments. 33 | pub fn cmd(dir: &str, cmd: &str, args: &[&str]) -> io::Result<()> { 34 | Command::new(cmd) 35 | .args(args) 36 | .current_dir(dir) 37 | .stdout(Stdio::null()) 38 | .stderr(Stdio::null()) 39 | .spawn()? 40 | .wait()?; 41 | Ok(()) 42 | } 43 | 44 | /// Purges a location on disk, similar to `rm -rf`. 45 | pub fn del(parent: &str, child: &str) -> io::Result<()> { 46 | let path = format!("{}/{}", parent, child); 47 | 48 | // check for errors that we're ok with 49 | if let Err(err) = fs::remove_dir_all(path) { 50 | // if already gone, happy days are upon us 51 | if err.kind() == ErrorKind::NotFound { 52 | return Ok(()); 53 | } 54 | 55 | // if there's a permission error, we don't care 56 | if err.kind() == ErrorKind::PermissionDenied { 57 | return Ok(()); 58 | } 59 | 60 | // others, bad! 61 | return Err(err); 62 | } 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /src/cleaner/cargo.rs: -------------------------------------------------------------------------------- 1 | //! Basic cleaner module for Cargo projects. 2 | use super::Cleaner; 3 | use std::io; 4 | 5 | /// Cleaner implementation for Cargo projects. 6 | pub struct CargoCleaner; 7 | impl Cleaner for CargoCleaner { 8 | /// Returns the name of this cleaner. 9 | fn name(&self) -> &str { 10 | "Cargo" 11 | } 12 | 13 | /// Returns the triggers associated with this cleaner. 14 | fn triggers(&self) -> &[&str] { 15 | &["Cargo.toml"] 16 | } 17 | 18 | /// cleaner the provided directory based on a Cargo structure. 19 | fn clean(&self, dir: &str) -> io::Result<()> { 20 | super::cmd(dir, "cargo", &["clean"])?; 21 | super::del(dir, "target") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/cleaner/git.rs: -------------------------------------------------------------------------------- 1 | //! Basic cleaner module for Git projects. 2 | use super::Cleaner; 3 | use std::io; 4 | 5 | /// Cleaner implementation for Git projects. 6 | pub struct GitCleaner; 7 | impl Cleaner for GitCleaner { 8 | /// Returns the name of this cleaner. 9 | fn name(&self) -> &str { 10 | "Git" 11 | } 12 | 13 | /// Returns the triggers associated with this cleaner. 14 | fn triggers(&self) -> &[&str] { 15 | &[".git"] 16 | } 17 | 18 | /// Cleans the provided directory based on a Git structure. 19 | fn clean(&self, dir: &str) -> io::Result<()> { 20 | super::cmd(dir, "git", &["reflog", "expire", "--all", "--expire=now"])?; 21 | super::cmd(dir, "git", &["gc", "-q", "--prune=now", "--aggressive"]) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/cleaner/gradle.rs: -------------------------------------------------------------------------------- 1 | //! Basic cleaner module for Gradle projects. 2 | use super::Cleaner; 3 | use std::io; 4 | 5 | /// Cleaner implementation for Gradle projects. 6 | pub struct GradleCleaner; 7 | impl Cleaner for GradleCleaner { 8 | /// Returns the name of this cleaner. 9 | fn name(&self) -> &str { 10 | "Gradle" 11 | } 12 | 13 | /// Returns the triggers associated with this cleaner. 14 | fn triggers(&self) -> &[&str] { 15 | &["build.gradle"] 16 | } 17 | 18 | /// Cleans the provided directory based on a Git structure. 19 | fn clean(&self, dir: &str) -> io::Result<()> { 20 | super::cmd(dir, "gradle", &["clean"])?; 21 | super::del(dir, "build")?; 22 | super::del(dir, "out") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/cleaner/maven.rs: -------------------------------------------------------------------------------- 1 | //! Basic cleaner module for Maven projects. 2 | use super::Cleaner; 3 | use std::io; 4 | 5 | /// Cleaner implementation for Maven projects. 6 | pub struct MavenCleaner; 7 | impl Cleaner for MavenCleaner { 8 | /// Returns the name of this cleaner. 9 | fn name(&self) -> &str { 10 | "Maven" 11 | } 12 | 13 | /// Returns the triggers associated with this cleaner. 14 | fn triggers(&self) -> &[&str] { 15 | &["pom.xml"] 16 | } 17 | 18 | /// Cleans the provided directory based on a Git structure. 19 | fn clean(&self, dir: &str) -> io::Result<()> { 20 | super::cmd(dir, "mvn", &["clean"])?; 21 | super::del(dir, "target") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/cleaner/mix.rs: -------------------------------------------------------------------------------- 1 | //! Basic cleaner module for Mix projects. 2 | use super::Cleaner; 3 | use std::io; 4 | 5 | /// Cleaner implementation for Mix projects. 6 | pub struct MixCleaner; 7 | impl Cleaner for MixCleaner { 8 | /// Returns the name of this cleaner. 9 | fn name(&self) -> &str { 10 | "Mix" 11 | } 12 | 13 | /// Returns the triggers associated with this cleaner. 14 | fn triggers(&self) -> &[&str] { 15 | &["mix.exs"] 16 | } 17 | 18 | /// Cleans the provided directory based on a Git structure. 19 | fn clean(&self, dir: &str) -> io::Result<()> { 20 | super::cmd(dir, "mix", &["clean"])?; 21 | super::del(dir, "build")?; 22 | super::del(dir, "deps")?; 23 | super::del(dir, "doc") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/cleaner/node.rs: -------------------------------------------------------------------------------- 1 | //! Basic cleaner module for Node.js projects. 2 | use super::Cleaner; 3 | use std::io; 4 | 5 | /// Cleaner implementation for Node.js projects. 6 | pub struct NodeCleaner; 7 | impl Cleaner for NodeCleaner { 8 | /// Returns the name of this cleaner. 9 | fn name(&self) -> &str { 10 | "Node.js" 11 | } 12 | 13 | /// Returns the triggers associated with this cleaner. 14 | fn triggers(&self) -> &[&str] { 15 | &["package.json"] 16 | } 17 | 18 | /// Cleans the provided directory based on a Git structure. 19 | fn clean(&self, dir: &str) -> io::Result<()> { 20 | super::del(dir, "node_modules") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! Cleaning of unnecessary files in development directories. 2 | #![doc(html_root_url = "https://docs.rs/detox/0.1.2")] 3 | mod cleaner; 4 | mod options; 5 | 6 | use walkdir::WalkDir; 7 | 8 | use crate::options::Options; 9 | 10 | use std::env; 11 | use std::error::Error; 12 | use std::path::Path; 13 | 14 | fn main() -> Result<(), Box> { 15 | // parse in our options from the command line args 16 | let options = Options::from(&mut env::args_os()); 17 | 18 | // iterate each provided location 19 | for location in &options.locations { 20 | // grab the size of the location before we start 21 | let start = get_size(location); 22 | 23 | // iterate all file entries that we come across in the recursive walk 24 | for entry in WalkDir::new(location).into_iter().filter_map(Result::ok) { 25 | // grab the full path 26 | let path = entry.path(); 27 | 28 | // fetch the file name 29 | let segment = path 30 | .file_name() 31 | .unwrap() 32 | .to_str() 33 | .expect("a segment should exist"); 34 | 35 | // walk through all cleaners 36 | for cleaner in &options.cleaners { 37 | // skip if the cleaner doesn't care 38 | if !cleaner.triggers().contains(&segment) { 39 | continue; 40 | } 41 | 42 | // grab the dir 43 | let dir = path 44 | .parent() 45 | .unwrap() 46 | .to_str() 47 | .expect("dir should be a str"); 48 | 49 | // clean the directory 50 | cleaner.clean(dir)?; 51 | } 52 | } 53 | 54 | // fetch the size of the location when done 55 | let end = get_size(location); 56 | 57 | // output the stats 58 | println!( 59 | "Reduced {} from {} to {} ({:.2}%)", 60 | location.display(), 61 | start, 62 | end, 63 | ((start - end) as f64 / start as f64) * 100.0 64 | ) 65 | } 66 | 67 | // done! 68 | Ok(()) 69 | } 70 | 71 | /// Determines the size of a directory on the filesystem. 72 | fn get_size(path: &Path) -> u64 { 73 | WalkDir::new(path) 74 | .into_iter() 75 | .filter_map(Result::ok) 76 | .filter(|node| node.file_type().is_file()) 77 | .filter_map(|file| file.metadata().ok()) 78 | .map(|meta| meta.len()) 79 | .sum() 80 | } 81 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | //! Options parsing and configuration for command line interfaces. 2 | use clap::{App, AppSettings, Arg}; 3 | use std::ffi::OsString; 4 | use std::path::PathBuf; 5 | 6 | use crate::cleaner::*; 7 | 8 | /// Options struct to store configuration state. 9 | pub struct Options { 10 | pub(crate) cleaners: Vec>, 11 | pub(crate) locations: Vec, 12 | } 13 | 14 | impl Options { 15 | /// Creates an `Options` struct from an iterable set of arguments. 16 | pub fn from(args: I) -> Options 17 | where 18 | I: IntoIterator, 19 | T: Into + Clone, 20 | { 21 | // create a new parser for our args 22 | let parser = Options::create_parser(); 23 | 24 | // parse out the arguments into matching opts 25 | let options = parser.get_matches_from(args); 26 | 27 | // create opts 28 | Options { 29 | cleaners: vec![ 30 | Box::new(GitCleaner), 31 | Box::new(GradleCleaner), 32 | Box::new(MixCleaner), 33 | Box::new(NodeCleaner), 34 | Box::new(MavenCleaner), 35 | Box::new(CargoCleaner), 36 | ], 37 | locations: options 38 | .values_of("locations") 39 | .unwrap() 40 | .filter_map(|location| PathBuf::from(location).canonicalize().ok()) 41 | .collect(), 42 | } 43 | } 44 | 45 | /// Creates a parser used to generate `Options`. 46 | fn create_parser<'a, 'b>() -> App<'a, 'b> { 47 | App::new("") 48 | // package metadata from cargo 49 | .name(env!("CARGO_PKG_NAME")) 50 | .about(env!("CARGO_PKG_DESCRIPTION")) 51 | .version(env!("CARGO_PKG_VERSION")) 52 | // arguments and flag details 53 | .args(&[ 54 | // inputs: +required +multiple 55 | Arg::with_name("locations") 56 | .help("Locations to search for cleanup") 57 | .multiple(true) 58 | .required(true), 59 | ]) 60 | // settings required for parsing 61 | .settings(&[ 62 | AppSettings::ArgRequiredElseHelp, 63 | AppSettings::HidePossibleValuesInHelp, 64 | AppSettings::TrailingVarArg, 65 | ]) 66 | } 67 | } 68 | --------------------------------------------------------------------------------