Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to clap 4 #87

Merged
merged 8 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 223 additions & 55 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository = "https://github.com/holmgr/cargo-sweep"
readme = "README.md"

[dependencies]
clap = "2.32.0"
clap = { version = "4.0.32", features = ["derive"] }
crossterm = "0.25.0"
walkdir = "2"
anyhow = "1.0.43"
Expand Down
163 changes: 163 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use clap::{ArgGroup, Parser};
use std::path::PathBuf;

pub fn parse() -> Args {
SweepArgs::parse().into_args()
}

#[derive(Parser)]
#[command(version, about)]
pub struct SweepArgs {
#[command(subcommand)]
sweep: SweepCommand,
}

impl SweepArgs {
fn into_args(self) -> Args {
let SweepCommand::Sweep(args) = self.sweep;
args
}
}

#[derive(clap::Subcommand)]
pub enum SweepCommand {
Sweep(Args),
}

#[derive(Parser, Debug)]
#[cfg_attr(test, derive(Default, PartialEq))]
#[command(
about,
version,
group(
ArgGroup::new("criterion")
.required(true)
.args(["stamp", "file", "time", "installed", "toolchains", "maxsize"])
)
)]
pub struct Args {
/// Path to check
pub path: Option<PathBuf>,

/// Dry run which will not delete any files
#[arg(short, long)]
pub dry_run: bool,

/// Load timestamp file in the given path, cleaning everything older
#[arg(short, long)]
file: bool,

#[arg(
long,
help = "The `recursive` flag defaults to ignoring directories \
that start with a `.`, `.git` for example is unlikely to include a \
Cargo project, this flag changes it to look in them"
)]
pub hidden: bool,

/// Keep only artifacts made by Toolchains currently installed by rustup
#[arg(short, long)]
installed: bool,

/// Remove oldest artifacts until the target directory is below the specified size in MB
///
/// TODO: consider parsing units like GB, KB
/// https://github.com/holmgr/cargo-sweep/issues/82
#[arg(short, long, value_name = "MAXSIZE_MB")]
maxsize: Option<u64>,

/// Apply on all projects below the given path
#[arg(short, long)]
pub recursive: bool,

/// Store timestamp file at the given path, is used by file option
#[arg(short, long)]
stamp: bool,

/// Number of days backwards to keep
#[arg(short, long)]
time: Option<u64>,

/// Toolchains currently installed by rustup that should have their artifacts kept
#[arg(long, value_delimiter = ',')]
toolchains: Vec<String>,

/// Turn verbose information on
#[arg(short, long)]
pub verbose: bool,
}

impl Args {
pub fn criterion(&self) -> Criterion {
match &self {
_ if self.stamp => Criterion::Stamp,
_ if self.file => Criterion::File,
_ if self.installed => Criterion::Installed,
_ if !self.toolchains.is_empty() => Criterion::Toolchains(self.toolchains.clone()),
jyn514 marked this conversation as resolved.
Show resolved Hide resolved
Self {
time: Some(time), ..
} => Criterion::Time(*time),
Self {
maxsize: Some(size),
..
} => Criterion::MaxSize(*size),
_ => unreachable!("guaranteed by clap ArgGroup"),
}
}
}

pub enum Criterion {
Stamp,
File,
Time(u64),
Installed,
Toolchains(Vec<String>),
MaxSize(u64),
}

#[cfg(test)]
mod tests {
use super::*;

/// Test helper for splitting arguments and providing them to clap
fn parse(command: &str) -> Result<Args, clap::Error> {
let command_args = command.split_whitespace();
dbg!(SweepArgs::try_parse_from(command_args).map(SweepArgs::into_args))
}

#[test]
fn test_argparsing() {
// Argument is required
assert!(parse("cargo sweep").is_err());
assert!(parse("cargo sweep --installed").is_ok());
assert!(parse("cargo sweep --file").is_ok());
assert!(parse("cargo sweep --stamp").is_ok());
assert!(parse("cargo sweep --time 30").is_ok());
assert!(parse("cargo sweep --toolchains SAMPLE_TEXT").is_ok());
assert!(parse("cargo sweep --maxsize 100").is_ok());

assert!(parse("cargo-sweep sweep").is_err());
assert!(parse("cargo-sweep sweep --installed").is_ok());
assert!(parse("cargo-sweep sweep --file").is_ok());
assert!(parse("cargo-sweep sweep --stamp").is_ok());
assert!(parse("cargo-sweep sweep --time 30").is_ok());
assert!(parse("cargo-sweep sweep --toolchains SAMPLE_TEXT").is_ok());
assert!(parse("cargo-sweep sweep --maxsize 100").is_ok());

// Argument conflicts
assert!(parse("cargo sweep --installed --maxsize 100").is_err());
assert!(parse("cargo sweep --file --installed").is_err());
assert!(parse("cargo sweep --stamp --file").is_err());
assert!(parse("cargo sweep --time 30 --stamp").is_err());
assert!(parse("cargo sweep --toolchains SAMPLE_TEXT --time 30").is_err());
assert!(parse("cargo sweep --maxsize 100 --toolchains SAMPLE_TEXT").is_err());

// Test if comma separated list is parsed correctly
let args = Args {
toolchains: ["1", "2", "3"].map(ToString::to_string).to_vec(),
..Args::default()
};
assert_eq!(args, parse("cargo sweep --toolchains 1,2,3").unwrap());
assert!(parse("cargo sweep --toolchains 1 2 3").is_err());
marcospb19 marked this conversation as resolved.
Show resolved Hide resolved
}
}
8 changes: 4 additions & 4 deletions src/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,17 @@ fn rustup_toolchain_list() -> Option<Vec<String>> {

pub fn remove_not_built_with(
dir: &Path,
rust_version_to_keep: Option<&str>,
rust_version_to_keep: &[String],
dry_run: bool,
) -> Result<u64, Error> {
debug!("cleaning: {:?} with remove_not_built_with", dir);
let mut total_disk_space = 0;
let hashed_rust_version_to_keep = if let Some(names) = rust_version_to_keep {
let hashed_rust_version_to_keep = if !rust_version_to_keep.is_empty() {
info!(
"Using specified installed toolchains: {:?}",
names.split(',').collect::<Vec<_>>()
rust_version_to_keep
);
lookup_from_names(names.split(',').map(Some))?
lookup_from_names(rust_version_to_keep.iter().map(Some))?
} else {
match rustup_toolchain_list() {
Some(list) => {
Expand Down
Loading