From b714db1952fcfcd5f688feab148792a4162effb6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 25 Jun 2023 20:05:03 +1200 Subject: [PATCH] Update Clap to v4 --- Cargo.lock | 169 +++++++++++++++++++++++++++++++------ Cargo.toml | 2 +- src/main.rs | 233 ++++++++++++++++++++++------------------------------ 3 files changed, 245 insertions(+), 159 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6dc42816..9d34640f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,55 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "atty" version = "0.2.14" @@ -69,27 +118,31 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.2.25" +version = "4.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" dependencies = [ - "atty", + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" +dependencies = [ + "anstream", + "anstyle", "bitflags", "clap_lex", - "indexmap", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "color_quant" @@ -97,6 +150,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "crc32fast" version = "1.3.2" @@ -155,6 +214,27 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fdeflate" version = "0.3.0" @@ -222,6 +302,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "image" version = "0.24.6" @@ -247,6 +333,29 @@ dependencies = [ "rayon", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "libc" version = "0.2.144" @@ -271,6 +380,12 @@ dependencies = [ "libdeflate-sys", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "log" version = "0.4.17" @@ -345,12 +460,6 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "oxipng" version = "8.0.0" @@ -446,6 +555,20 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -497,12 +620,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thread_local" version = "1.1.7" @@ -519,6 +636,12 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "wild" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0dea7d51..690473d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ version = "1.7.0" [dependencies.clap] optional = true -version = "3.2.25" +version = "4.3.8" [dependencies.wild] optional = true diff --git a/src/main.rs b/src/main.rs index 71c66fa3..d848d5ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ #![warn(clippy::range_plus_one)] #![allow(clippy::cognitive_complexity)] -use clap::{AppSettings, Arg, ArgAction, ArgMatches, Command}; +use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; use indexmap::IndexSet; use log::{error, warn}; use oxipng::Deflaters; @@ -33,49 +33,43 @@ fn main() { .version(env!("CARGO_PKG_VERSION")) .author("Joshua Holmer ") .about("Losslessly improves compression of PNG files") - .setting(AppSettings::DeriveDisplayOrder) .arg( Arg::new("files") .help("File(s) to compress (use \"-\" for stdin)") .index(1) - .multiple_values(true) + .num_args(1..) .use_value_delimiter(false) - .required(true), + .required(true) + .value_parser(value_parser!(PathBuf)), ) .arg( Arg::new("optimization") .help("Optimization level - Default: 2") .short('o') .long("opt") - .takes_value(true) .value_name("level") - .possible_value("0") - .possible_value("1") - .possible_value("2") - .possible_value("3") - .possible_value("4") - .possible_value("5") - .possible_value("6") - .possible_value("max"), + .value_parser(["0", "1", "2", "3", "4", "5", "6", "max"]), ) .arg( Arg::new("backup") .help("Back up modified files") .short('b') - .long("backup"), + .long("backup") + .action(ArgAction::SetTrue), ) .arg( Arg::new("recursive") .help("Recurse into subdirectories") .short('r') - .long("recursive"), + .long("recursive") + .action(ArgAction::SetTrue), ) .arg( Arg::new("output_dir") .help("Write output file(s) to ") .long("dir") - .takes_value(true) .value_name("directory") + .value_parser(value_parser!(PathBuf)) .conflicts_with("output_file") .conflicts_with("stdout"), ) @@ -83,8 +77,8 @@ fn main() { Arg::new("output_file") .help("Write output file to ") .long("out") - .takes_value(true) .value_name("file") + .value_parser(value_parser!(PathBuf)) .conflicts_with("output_dir") .conflicts_with("stdout"), ) @@ -92,6 +86,7 @@ fn main() { Arg::new("stdout") .help("Write output to stdout") .long("stdout") + .action(ArgAction::SetTrue) .conflicts_with("output_dir") .conflicts_with("output_file"), ) @@ -99,31 +94,34 @@ fn main() { Arg::new("preserve") .help("Preserve file attributes if possible") .short('p') - .long("preserve"), + .long("preserve") + .action(ArgAction::SetTrue), ) .arg( Arg::new("check") .help("Do not run any optimization passes") .short('c') - .long("check"), + .long("check") + .action(ArgAction::SetTrue), ) .arg( Arg::new("pretend") .help("Do not write any files, only calculate compression gains") .short('P') - .long("pretend"), + .long("pretend") + .action(ArgAction::SetTrue), ) .arg( Arg::new("strip-safe") .help("Strip safely-removable metadata objects") .short('s') + .action(ArgAction::SetTrue) .conflicts_with("strip"), ) .arg( Arg::new("strip") .help("Strip metadata objects ['safe', 'all', or comma-separated list]") .long("strip") - .takes_value(true) .value_name("mode") .conflicts_with("strip-safe"), ) @@ -131,7 +129,6 @@ fn main() { Arg::new("keep") .help("Strip all optional metadata except objects in the comma-separated list") .long("keep") - .takes_value(true) .value_name("list") .conflicts_with("strip") .conflicts_with("strip-safe"), @@ -140,23 +137,22 @@ fn main() { Arg::new("alpha") .help("Perform additional alpha optimizations") .short('a') - .long("alpha"), + .long("alpha") + .action(ArgAction::SetTrue), ) .arg( Arg::new("interlace") .help("PNG interlace type - Default: 0") .short('i') .long("interlace") - .takes_value(true) .value_name("type") - .possible_value("0") - .possible_value("1") - .possible_value("keep"), + .value_parser(["0", "1", "keep"]), ) .arg( Arg::new("scale16") .help("Forcibly reduce 16-bit images to 8-bit") - .long("scale16"), + .long("scale16") + .action(ArgAction::SetTrue), ) .arg( Arg::new("verbose") @@ -171,33 +167,29 @@ fn main() { .help("Run in quiet mode") .short('q') .long("quiet") + .action(ArgAction::SetTrue) .conflicts_with("verbose"), ) .arg( Arg::new("filters") - .help(&*format!( - "PNG delta filters (0-{}) - Default: 0,{}", - RowFilter::LAST, - RowFilter::MinSum as u8 - )) + .help(format!("PNG delta filters (0-{})", RowFilter::LAST)) .short('f') .long("filters") - .takes_value(true) - .validator(|x| match parse_numeric_range_opts(x, 0, RowFilter::LAST) { - Ok(_) => Ok(()), - Err(_) => Err("Invalid option for filters".to_owned()), + .value_parser(|x: &str| { + parse_numeric_range_opts(x, 0, RowFilter::LAST) + .map_err(|_| "Invalid option for filters") }), ) .arg( Arg::new("fast") .help("Use fast filter evaluation") - .long("fast"), + .long("fast") + .action(ArgAction::SetTrue), ) .arg( Arg::new("compression") - .help("zlib compression level (1-12) - Default: 11") + .help("zlib compression level (1-12)") .long("zc") - .takes_value(true) .value_name("level") .value_parser(1..=12) .conflicts_with("zopfli"), @@ -205,65 +197,72 @@ fn main() { .arg( Arg::new("no-bit-reduction") .help("No bit depth reduction") - .long("nb"), + .long("nb") + .action(ArgAction::SetTrue), ) .arg( Arg::new("no-color-reduction") .help("No color type reduction") - .long("nc"), + .long("nc") + .action(ArgAction::SetTrue), ) .arg( Arg::new("no-palette-reduction") .help("No palette reduction") - .long("np"), + .long("np") + .action(ArgAction::SetTrue), ) .arg( Arg::new("no-grayscale-reduction") .help("No grayscale reduction") - .long("ng"), + .long("ng") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("no-reductions") + .help("No reductions") + .long("nx") + .action(ArgAction::SetTrue), ) - .arg(Arg::new("no-reductions").help("No reductions").long("nx")) .arg( Arg::new("no-recoding") .help("No IDAT recoding unless necessary") - .long("nz"), + .long("nz") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("fix") + .help("Enable error recovery") + .long("fix") + .action(ArgAction::SetTrue), ) - .arg(Arg::new("fix").help("Enable error recovery").long("fix")) .arg( Arg::new("force") .help("Write the output even if it is larger than the input") - .long("force"), + .long("force") + .action(ArgAction::SetTrue), ) .arg( Arg::new("zopfli") .help("Use the slower but better compressing Zopfli algorithm") .short('Z') - .long("zopfli"), + .long("zopfli") + .action(ArgAction::SetTrue), ) .arg( Arg::new("timeout") .help("Maximum amount of time, in seconds, to spend on optimizations") - .takes_value(true) .value_name("secs") - .long("timeout"), + .long("timeout") + .value_parser(value_parser!(u64)), ) .arg( Arg::new("threads") - .help("Set number of threads to use - default 1.5x CPU cores") + .help("Set number of threads to use - Default: num CPU cores") .long("threads") .short('t') - .takes_value(true) .value_name("num") - .validator(|x| match x.parse::() { - Ok(val) => { - if val > 0 { - Ok(()) - } else { - Err("Thread count must be >= 1".to_owned()) - } - } - Err(_) => Err("Thread count must be >= 1".to_owned()), - }), + .value_parser(value_parser!(usize)), ) .after_help( "Optimization levels: @@ -304,13 +303,13 @@ Heuristic filter selection strategies: let files = collect_files( matches - .values_of("files") + .get_many::("files") .unwrap() - .map(PathBuf::from) + .cloned() .collect(), &out_dir, &out_file, - matches.is_present("recursive"), + matches.get_flag("recursive"), true, ); @@ -387,19 +386,19 @@ fn parse_opts_into_struct( ) -> Result<(OutFile, Option, Options), String> { stderrlog::new() .module(module_path!()) - .quiet(matches.is_present("quiet")) + .quiet(matches.get_flag("quiet")) .verbosity(matches.get_count("verbose") as usize + 2) .show_level(false) .init() .unwrap(); - let mut opts = match matches.value_of("optimization") { + let mut opts = match matches.get_one::("optimization") { None => Options::default(), - Some("max") => Options::max_compression(), + Some(x) if x == "max" => Options::max_compression(), Some(level) => Options::from_preset(level.parse::().unwrap()), }; - if let Some(x) = matches.value_of("interlace") { + if let Some(x) = matches.get_one::("interlace") { opts.interlace = if x == "keep" { None } else { @@ -407,110 +406,76 @@ fn parse_opts_into_struct( }; } - if let Some(x) = matches.value_of("filters") { + if let Some(x) = matches.get_one::>("filters") { opts.filter.clear(); - for f in parse_numeric_range_opts(x, 0, RowFilter::LAST).unwrap() { + for &f in x { opts.filter.insert(f.try_into().unwrap()); } } - if let Some(x) = matches.value_of("timeout") { - let num = x - .parse() - .map_err(|_| "Timeout must be a number".to_owned())?; + if let Some(&num) = matches.get_one::("timeout") { opts.timeout = Some(Duration::from_secs(num)); } - let out_dir = if let Some(x) = matches.value_of("output_dir") { - let path = PathBuf::from(x); + let out_dir = if let Some(path) = matches.get_one::("output_dir") { if !path.exists() { - match DirBuilder::new().recursive(true).create(&path) { + match DirBuilder::new().recursive(true).create(path) { Ok(_) => (), Err(x) => return Err(format!("Could not create output directory {}", x)), }; } else if !path.is_dir() { return Err(format!( "{} is an existing file (not a directory), cannot create directory", - x + path.display() )); } - Some(path) + Some(path.to_owned()) } else { None }; - let out_file = if matches.is_present("stdout") { + let out_file = if matches.get_flag("stdout") { OutFile::StdOut - } else if let Some(x) = matches.value_of("output_file") { - OutFile::Path(Some(PathBuf::from(x))) } else { - OutFile::Path(None) + OutFile::Path(matches.get_one::("output_file").cloned()) }; - if matches.is_present("alpha") { - opts.optimize_alpha = true; - } + opts.optimize_alpha = matches.get_flag("alpha"); - if matches.is_present("scale16") { - opts.scale_16 = true; - } + opts.scale_16 = matches.get_flag("scale16"); - if matches.is_present("fast") { - opts.fast_evaluation = true; - } + opts.fast_evaluation = matches.get_flag("fast"); - if matches.is_present("backup") { - opts.backup = true; - } + opts.backup = matches.get_flag("backup"); - if matches.is_present("force") { - opts.force = true; - } + opts.force = matches.get_flag("force"); - if matches.is_present("fix") { - opts.fix_errors = true; - } + opts.fix_errors = matches.get_flag("fix"); - if matches.is_present("check") { - opts.check = true; - } + opts.check = matches.get_flag("check"); - if matches.is_present("pretend") { - opts.pretend = true; - } + opts.pretend = matches.get_flag("pretend"); - if matches.is_present("preserve") { - opts.preserve_attrs = true; - } + opts.preserve_attrs = matches.get_flag("preserve"); - if matches.is_present("no-bit-reduction") { - opts.bit_depth_reduction = false; - } + opts.bit_depth_reduction = !matches.get_flag("no-bit-reduction"); - if matches.is_present("no-color-reduction") { - opts.color_type_reduction = false; - } + opts.color_type_reduction = !matches.get_flag("no-color-reduction"); - if matches.is_present("no-palette-reduction") { - opts.palette_reduction = false; - } + opts.palette_reduction = !matches.get_flag("no-palette-reduction"); - if matches.is_present("no-grayscale-reduction") { - opts.grayscale_reduction = false; - } + opts.grayscale_reduction = !matches.get_flag("no-grayscale-reduction"); - if matches.is_present("no-reductions") { + if matches.get_flag("no-reductions") { opts.bit_depth_reduction = false; opts.color_type_reduction = false; opts.palette_reduction = false; opts.grayscale_reduction = false; } - if matches.is_present("no-recoding") { - opts.idat_recoding = false; - } + opts.idat_recoding = !matches.get_flag("no-recoding"); - if let Some(keep) = matches.value_of("keep") { + if let Some(keep) = matches.get_one::("keep") { let names = keep .split(',') .map(parse_chunk_name) @@ -518,7 +483,7 @@ fn parse_opts_into_struct( opts.strip = StripChunks::Keep(names) } - if let Some(strip) = matches.value_of("strip") { + if let Some(strip) = matches.get_one::("strip") { if strip == "safe" { opts.strip = StripChunks::Safe; } else if strip == "all" { @@ -546,11 +511,11 @@ fn parse_opts_into_struct( } } - if matches.is_present("strip-safe") { + if matches.get_flag("strip-safe") { opts.strip = StripChunks::Safe; } - if matches.is_present("zopfli") { + if matches.get_flag("zopfli") { #[cfg(feature = "zopfli")] if let Some(iterations) = NonZeroU8::new(15) { opts.deflate = Deflaters::Zopfli { iterations }; @@ -562,9 +527,7 @@ fn parse_opts_into_struct( } #[cfg(feature = "parallel")] - if let Some(x) = matches.value_of("threads") { - let threads = x.parse::().unwrap(); - + if let Some(&threads) = matches.get_one::("threads") { rayon::ThreadPoolBuilder::new() .num_threads(threads) .build_global()