diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e21a3d..df223a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,9 +27,8 @@ All notable changes to this project will be documented in this file. - More commonalities and simplification - Remove TimeStamp (#113) -- Lint fixes -- Clean up lints -- Remove lint +- Lint fix (#116) +- Switch to Clap v4 ### Build diff --git a/Cargo.lock b/Cargo.lock index 9d04907..4e5dc34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,32 +171,31 @@ dependencies = [ "atty", "bitflags", "strsim 0.8.0", - "textwrap 0.11.0", + "textwrap", "unicode-width", "vec_map", ] [[package]] name = "clap" -version = "3.1.12" +version = "4.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c167e37342afc5f33fd87bbc870cedd020d2a6dffa05d45ccd9241fbdd146db" +checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" dependencies = [ - "atty", "bitflags", "clap_lex", - "indexmap", - "lazy_static", + "is-terminal", + "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "terminal_size", ] [[package]] name = "clap_lex" -version = "0.1.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" dependencies = [ "os_str_bytes", ] @@ -330,10 +329,10 @@ dependencies = [ [[package]] name = "fit2csv" -version = "0.4.1" +version = "0.5.0" dependencies = [ "assay", - "clap 3.1.12", + "clap 4.1.13", "env_logger", "log", "utilities", @@ -365,11 +364,11 @@ dependencies = [ [[package]] name = "fitrename" -version = "0.3.1" +version = "0.4.0" dependencies = [ "assay", "chrono", - "clap 3.1.12", + "clap 4.1.13", "env_logger", "fitparser", "gpx", @@ -380,11 +379,11 @@ dependencies = [ [[package]] name = "fitview" -version = "0.3.1" +version = "0.4.0" dependencies = [ "assay", "chrono", - "clap 3.1.12", + "clap 4.1.13", "env_logger", "log", "utilities", @@ -439,21 +438,15 @@ dependencies = [ [[package]] name = "gpx2csv" -version = "0.3.1" +version = "0.4.0" dependencies = [ "assay", - "clap 3.1.12", + "clap 4.1.13", "env_logger", "log", "utilities", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - [[package]] name = "heck" version = "0.3.3" @@ -484,16 +477,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "indexmap" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.12" @@ -647,9 +630,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "os_str_bytes" @@ -926,10 +909,10 @@ dependencies = [ [[package]] name = "tcx2csv" -version = "0.2.2" +version = "0.3.0" dependencies = [ "assay", - "clap 3.1.12", + "clap 4.1.13", "env_logger", "log", "tcx", @@ -960,19 +943,23 @@ dependencies = [ ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "terminal_size" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a" dependencies = [ - "unicode-width", + "rustix", + "windows-sys", ] [[package]] name = "textwrap" -version = "0.15.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] [[package]] name = "thiserror" @@ -1054,11 +1041,11 @@ dependencies = [ [[package]] name = "utilities" -version = "0.3.0" +version = "0.4.0" dependencies = [ "assay", "chrono", - "clap 3.1.12", + "clap 4.1.13", "convert_case", "csv", "env_logger", diff --git a/fit2csv/Cargo.toml b/fit2csv/Cargo.toml index 0820389..3f0d430 100644 --- a/fit2csv/Cargo.toml +++ b/fit2csv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fit2csv" -version = "0.4.1" +version = "0.5.0" description = "Parses .FIT files to .JSON and .CSV" license = "Apache-2.0" authors = ["evensolberg "] @@ -8,7 +8,7 @@ edition = "2021" include = ["src/**/*", "README.md"] [dependencies] -clap = { version = "3.1.12", features = ["cargo", "color"] } +clap = { version = "4.1.9", features = ["cargo", "wrap_help", "env"] } env_logger = "0.10.0" log = "0.4.16" diff --git a/fit2csv/src/cli.rs b/fit2csv/src/cli.rs index 5f6343c..570ecec 100644 --- a/fit2csv/src/cli.rs +++ b/fit2csv/src/cli.rs @@ -1,5 +1,5 @@ //! Contains a single function to build the CLI -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; /// Builds the CLI so the main file doesn't get cluttered. pub fn build() -> ArgMatches { @@ -12,52 +12,54 @@ pub fn build() -> ArgMatches { Arg::new("read") .value_name("FILE(S)") .help("One or more .fit file(s) to process. Wildcards and multiple_occurrences files (e.g. 2019*.fit 2020*.fit) are supported.") - .takes_value(true) + .num_args(1..) .required(true) - .multiple_occurrences(true), + .action(ArgAction::Append) ) .arg( // Hidden debug parameter Arg::new("debug") .short('d') .long("debug") - .multiple_occurrences(true) .help("Output debug information as we go. Supply it twice for trace-level logs.") - .takes_value(false) + .env("FIT_DEBUG") + .num_args(0) + .action(ArgAction::Count) .hide(true), ) .arg( // Don't print any information Arg::new("quiet") .short('q') .long("quiet") - .multiple_occurrences(false) .help("Don't produce any output except errors while working.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Print summary information Arg::new("print-summary") .short('p') .long("print-summary") - .multiple_occurrences(false) .help("Print summary detail for each session processed.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Don't export detail information Arg::new("detail-off") .short('o') .long("detail-off") - .multiple_occurrences(false) .help("Don't export detailed information from each file parsed.") - .takes_value(false) .requires("summary-file") + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Summary file name Arg::new("summary-file") .short('s') .value_name("summary output file name") .long("summary-file") - .multiple_occurrences(false) .help("Summary output file name.") - .takes_value(true) + .num_args(1) + .required(false) + .action(ArgAction::Set) ) .get_matches() } diff --git a/fit2csv/src/main.rs b/fit2csv/src/main.rs index 3f83a2b..c72498a 100644 --- a/fit2csv/src/main.rs +++ b/fit2csv/src/main.rs @@ -2,6 +2,8 @@ use env_logger::Target; use std::error::Error; use utilities::{FITActivities, FITActivity}; +use clap::parser::ValueSource; + mod cli; ////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,19 +17,25 @@ fn run() -> Result<(), Box> { logbuilder.target(Target::Stdout).init(); // If tracing, output the names of the files being processed - for argument in cli_args.values_of("read").unwrap_or_default() { + for argument in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::trace!("main::run() -- Arguments: {argument:?}"); } // Find the name of the session output file + let session_file_name = String::from("fit-sessions.csv"); let sessionfile = cli_args - .value_of("summary-file") - .unwrap_or("fit-sessions.csv"); + .get_one::("summary-file") + .unwrap_or(&session_file_name) + .as_str(); log::debug!("main::run() -- session output file: {sessionfile}"); // Let the user know if we're writing - if cli_args.is_present("detail-off") { - log::info!("Writing summary file {} only.", &sessionfile); + if cli_args.value_source("detail-off") == Some(ValueSource::CommandLine) { + log::info!("Writing summary file {sessionfile} only."); } else { log::info!("Writing detail files."); } @@ -38,19 +46,23 @@ fn run() -> Result<(), Box> { // Create an empty placeholder for all the activities let mut activities = FITActivities::new(); - for filename in cli_args.values_of("read").unwrap_or_default() { + for filename in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::info!("Processing file: {filename}"); // Parse the FIT file let activity = FITActivity::from_file(filename)?; // Output the files - if cli_args.is_present("print-summary") { + if cli_args.value_source("print-summary") == Some(ValueSource::CommandLine) { activity.session.print_summary(); } // Export the data if requested - if !cli_args.is_present("detail-off") { + if cli_args.value_source("detail-off") != Some(ValueSource::CommandLine) { activity.export()?; } @@ -59,8 +71,8 @@ fn run() -> Result<(), Box> { } // Export the summary information - if cli_args.is_present("summary-file") { - log::info!("Summary information written to: {}", &sessionfile); + if cli_args.value_source("summary-file") == Some(ValueSource::CommandLine) { + log::info!("Summary information written to: {sessionfile}"); activities.export_summary_csv(sessionfile)?; } diff --git a/fitrename/Cargo.toml b/fitrename/Cargo.toml index b3a749e..8b81662 100644 --- a/fitrename/Cargo.toml +++ b/fitrename/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "fitrename" -version = "0.3.1" +version = "0.4.0" edition = "2021" description = "Renames .FIT, .GPX and .TCX files based on metadata." license = "Apache-2.0" @@ -12,7 +12,7 @@ include = ["src/**/*", "README.md"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "3.1.12", features = ["cargo", "color"] } +clap = { version = "4.1.9", features = ["cargo", "wrap_help", "env"] } env_logger = "0.10.0" log = "0.4.16" diff --git a/fitrename/src/cli.rs b/fitrename/src/cli.rs index 49fdaa9..ae4582d 100644 --- a/fitrename/src/cli.rs +++ b/fitrename/src/cli.rs @@ -1,5 +1,5 @@ //! Contains a single function to build the CLI -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; /// Builds the CLI so the main file doesn't get cluttered. pub fn build() -> ArgMatches { @@ -12,17 +12,17 @@ pub fn build() -> ArgMatches { Arg::new("read") .value_name("FILE(S)") .help("One or more .fit, .gpx or .tcx file(s) to process. Wildcards and multiple_occurrences files (e.g. 2019*.fit 2020*.gpx) are supported.") - .takes_value(true) + .num_args(1..) .required(true) - .multiple_occurrences(true), + .action(ArgAction::Append), ) .arg( // Rename pattern} Arg::new("pattern") .short('p') .long("pattern") .help("The pattern for new file names.") - .multiple_occurrences(false) - .takes_value(true) + .num_args(1) + .action(ArgAction::Set) .required(true) .hide(false), ) @@ -31,33 +31,34 @@ pub fn build() -> ArgMatches { .short('d') .long("debug") .help("Output debug information as we go. Supply it twice for trace-level logs.") - .multiple_occurrences(true) - .takes_value(false) - .hide(true), + .env("FIT_DEBUG") + .hide(true) + .num_args(0) + .action(ArgAction::Count) ) .arg( // Don't print any information Arg::new("quiet") .short('q') .long("quiet") - .multiple_occurrences(false) .help("Don't produce any output except errors while working.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Print summary information Arg::new("print-summary") .short('s') .long("print-summary") .help("Print a summary of the number of files processed, errors, etc.") - .multiple_occurrences(false) - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Dry-run Arg::new("dry-run") .short('r') .long("dry-run") .help("Perform a dry-run. This will output what the result will be without performing the actual rename operation.") - .multiple_occurrences(false) - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .get_matches() } diff --git a/fitrename/src/main.rs b/fitrename/src/main.rs index 41ed218..53b244a 100644 --- a/fitrename/src/main.rs +++ b/fitrename/src/main.rs @@ -1,6 +1,8 @@ use env_logger::Target; use std::{collections::HashMap, error::Error, path::Path}; +use clap::parser::ValueSource; + mod cli; ////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,23 +17,35 @@ fn run() -> Result<(), Box> { let mut logbuilder = utilities::build_log(&cli_args); logbuilder.target(Target::Stdout).init(); - for argument in cli_args.values_of("read").unwrap_or_default() { + for argument in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::trace!("main::run() -- Arguments: {argument:?}"); } - let dry_run = cli_args.is_present("dry-run"); + let dry_run = cli_args.value_source("dry-run") == Some(ValueSource::CommandLine); if dry_run { log::info!("Dry-run. Will not perform actual rename."); } - let pattern = cli_args.value_of("pattern").unwrap_or_default(); + let default_pattern = String::new(); + let pattern = cli_args + .get_one::("pattern") + .unwrap_or(&default_pattern) + .as_str(); let mut total_files: usize = 0; let mut processed_files: usize = 0; let mut skipped_files: usize = 0; /////////////////////////////////// // Working section - for filename in cli_args.values_of("read").unwrap_or_default() { + for filename in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::debug!("Processing file: {filename}"); // Check if the target file exists, otherwise just continue @@ -82,7 +96,7 @@ fn run() -> Result<(), Box> { total_files += 1; } - if cli_args.is_present("print-summary") { + if cli_args.value_source("print-summary") == Some(ValueSource::CommandLine) { log::info!("Total files examined: {total_files:6}"); log::info!("Files processed: {processed_files:6}"); log::info!("Files skipped due to errors: {skipped_files:6}"); diff --git a/fitview/Cargo.toml b/fitview/Cargo.toml index 263c1a4..b69fb36 100644 --- a/fitview/Cargo.toml +++ b/fitview/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fitview" -version = "0.3.1" +version = "0.4.0" edition = "2021" description = "Shows the metadata from .FIT, .GPX and .TCX files." license = "Apache-2.0" @@ -11,7 +11,7 @@ include = ["src/**/*", "README.md"] [dependencies] chrono = { version = "0.4.19", features = ["serde"] } -clap = { version = "3.1.12", features = ["cargo", "color"] } +clap = { version = "4.1.9", features = ["cargo", "wrap_help", "env"] } env_logger = "0.10.0" log = "0.4.16" diff --git a/fitview/src/cli.rs b/fitview/src/cli.rs index 8f1935b..93a318c 100644 --- a/fitview/src/cli.rs +++ b/fitview/src/cli.rs @@ -1,5 +1,5 @@ //! Contains a single function to build the CLI -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; /// Builds the CLI so the main file doesn't get cluttered. pub fn build() -> ArgMatches { @@ -12,17 +12,18 @@ pub fn build() -> ArgMatches { Arg::new("read") .value_name("FILE(S)") .help("One or more .fit, .gpx or .tcx file(s) to process. Wildcards and multiple_occurrences files (e.g. 2019*.fit 2020*.gpx) are supported.") - .takes_value(true) + .num_args(1..) .required(true) - .multiple_occurrences(true), + .action(ArgAction::Append) ) .arg( // Hidden debug parameter Arg::new("debug") .short('d') .long("debug") .help("Output debug information as we go. Supply it twice for trace-level logs.") - .multiple_occurrences(true) - .takes_value(false) + .env("FIT_DEBUG") + .num_args(0) + .action(ArgAction::SetTrue) .hide(true), ) .arg( // Print summary information @@ -30,24 +31,22 @@ pub fn build() -> ArgMatches { .short('s') .long("print-summary") .help("Print a summary of the number of files processed, errors, etc.") - .multiple_occurrences(false) - .takes_value(false) + .num_args(0) ) .arg( // Print summary information Arg::new("print-detail") .short('l') .long("print-detail") .help("Print more detail for each file processed.") - .multiple_occurrences(false) - .takes_value(false) + .action(ArgAction::SetTrue) ) .arg( // Don't print any information Arg::new("quiet") .short('q') .long("quiet") - .multiple_occurrences(false) .help("Don't produce any output except errors while working.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) .hide(true) ) .get_matches() diff --git a/fitview/src/main.rs b/fitview/src/main.rs index 6450831..a12f70f 100644 --- a/fitview/src/main.rs +++ b/fitview/src/main.rs @@ -2,6 +2,8 @@ use env_logger::Target; use std::error::Error; use utilities::{FITActivity, GPXActivity, TCXActivity}; +use clap::parser::ValueSource; + mod cli; ////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -14,46 +16,54 @@ fn run() -> Result<(), Box> { let mut logbuilder = utilities::build_log(&cli_args); logbuilder.target(Target::Stdout).init(); - for argument in cli_args.values_of("read").unwrap_or_default() { - log::trace!("main::run() -- Arguments: {:?}", argument); + for files in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { + log::trace!("main::run() -- Files: {files}"); } let mut total_files: usize = 0; let mut processed_files: usize = 0; let skipped_files: usize = 0; - let detailed = cli_args.is_present("print-detail"); + let detailed = cli_args.value_source("print-detail") == Some(ValueSource::CommandLine); // The good stuff goes here - for filename in cli_args.values_of("read").unwrap_or_default() { - log::debug!("Processing file: {}", filename); + for filename in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { + log::debug!("Processing file: {filename}"); match utilities::get_extension(filename).as_ref() { "fit" => { - log::debug!("FIT: {}", filename); + log::debug!("FIT: {filename}"); let act = FITActivity::from_file(filename)?; act.print(detailed); processed_files += 1; } "gpx" => { - log::debug!("GPX: {}", filename); + log::debug!("GPX: {filename}"); let act = GPXActivity::from_file(filename)?; act.print(detailed); processed_files += 1; } "tcx" => { let act = TCXActivity::from_file(filename)?; - log::debug!("TCX: {}", filename); + log::debug!("TCX: {filename}"); act.print(detailed); } - _ => log::warn!("Unknown file type: {}.", &filename), + _ => log::warn!("Unknown file type: {filename}."), } total_files += 1; } - if cli_args.is_present("print-summary") { - log::info!("Total files examined: {:6}", total_files); - log::info!("Files processed: {:6}", processed_files); - log::info!("Files skipped due to errors: {:6}", skipped_files); + if cli_args.value_source("print-summary") == Some(ValueSource::CommandLine) { + log::info!("Total files examined: {total_files:6}"); + log::info!("Files processed: {processed_files:6}"); + log::info!("Files skipped due to errors: {skipped_files:6}"); } // Everything is a-okay in the end diff --git a/gpx2csv/Cargo.toml b/gpx2csv/Cargo.toml index 1814d2c..865be09 100644 --- a/gpx2csv/Cargo.toml +++ b/gpx2csv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gpx2csv" -version = "0.3.1" +version = "0.4.0" description = "Parses .GPX files to .JSON and .CSV" license = "Apache-2.0" authors = ["evensolberg "] @@ -10,7 +10,7 @@ include = ["src/**/*", "README.md"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "3.1.12", features = ["cargo", "color"] } +clap = { version = "4.1.9", features = ["cargo", "wrap_help"] } env_logger = "0.10.0" log = "0.4.16" diff --git a/gpx2csv/src/cli.rs b/gpx2csv/src/cli.rs index 4b67007..15b3fec 100644 --- a/gpx2csv/src/cli.rs +++ b/gpx2csv/src/cli.rs @@ -1,5 +1,5 @@ //! Contains a single function to build the CLI -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; /// Builds the CLI so the main file doesn't get cluttered. pub fn build() -> ArgMatches { @@ -12,34 +12,34 @@ pub fn build() -> ArgMatches { Arg::new("read") .value_name("FILE(S)") .help("One or more .gpx file(s) to process. Wildcards and multiple_occurrences files (e.g. 2019*.gpx 2020*.gpx) are supported.") - .takes_value(true) + .num_args(1..) .required(true) - .multiple_occurrences(true), + .action(ArgAction::Append) ) .arg( // Hidden debug parameter Arg::new("debug") .short('d') .long("debug") - .multiple_occurrences(true) .help("Output debug information as we go. Supply it twice for trace-level logs.") - .takes_value(false) + .num_args(0..) + .action(ArgAction::Count) .hide(true), ) .arg( // Don't print any information Arg::new("quiet") .short('q') .long("quiet") - .multiple_occurrences(false) .help("Don't produce any output except errors while working.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Don't export detail information Arg::new("detail-off") .short('o') .long("detail-off") - .multiple_occurrences(false) .help("Don't export detailed information from each file parsed.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) .requires("summary-file") ) .arg( // Summary file name @@ -47,9 +47,9 @@ pub fn build() -> ArgMatches { .short('s') .value_name("summary output file name") .long("summary-file") - .multiple_occurrences(false) .help("Summary output file name.") - .takes_value(true) + .num_args(1) + .action(ArgAction::Set) ) .get_matches() } diff --git a/gpx2csv/src/main.rs b/gpx2csv/src/main.rs index 7d48ea8..9cc646e 100644 --- a/gpx2csv/src/main.rs +++ b/gpx2csv/src/main.rs @@ -1,4 +1,5 @@ //! The main program file. +use clap::parser::ValueSource; use env_logger::Target; use std::error::Error; // Command line @@ -13,19 +14,25 @@ fn run() -> Result<(), Box> { let mut logbuilder = utilities::build_log(&cli_args); logbuilder.target(Target::Stdout).init(); - for argument in cli_args.values_of("read").unwrap_or_default() { - log::trace!("main::run() -- Arguments: {:?}", argument); + for filename in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { + log::trace!("main::run() -- Files to be read: {filename}"); } // Find the name of the session output file + let session_file_name = String::from("fit-sessions.csv"); let sessionfile = cli_args - .value_of("summary-only") - .unwrap_or("fit-sessions.csv"); - log::trace!("main::run() -- session output file: {}", sessionfile); + .get_one::("summary-only") + .unwrap_or(&session_file_name) + .as_str(); + log::trace!("main::run() -- session output file: {sessionfile}"); - // Let the user know if we're writing - if cli_args.is_present("detail-off") { - log::info!("Writing summary file {} only.", &sessionfile); + // Let the user know if we're writing details + if cli_args.value_source("detail-off") == Some(ValueSource::CommandLine) { + log::info!("Writing summary file {sessionfile} only."); } else { log::info!("Writing detail files."); } @@ -37,14 +44,18 @@ fn run() -> Result<(), Box> { let mut activities = utilities::GPXActivities::new(); // Do the parsing - for filename in cli_args.values_of("read").unwrap_or_default() { + for filename in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::info!("Processing file: {}", filename); // Extract the activity from the file let activity = utilities::GPXActivity::from_file(filename)?; // Export the data if requested - if !cli_args.is_present("detail-off") { + if cli_args.value_source("detail-off") != Some(ValueSource::CommandLine) { activity.export()?; // metadata, tracks, waypoints } @@ -53,8 +64,8 @@ fn run() -> Result<(), Box> { } // Export the summary list of activities - if cli_args.is_present("summary-file") { - log::info!("Summary information written to: {}", &sessionfile); + if cli_args.value_source("summary-file") == Some(ValueSource::CommandLine) { + log::info!("Summary information written to: {sessionfile}"); activities.export_csv(sessionfile)?; } diff --git a/tcx2csv/Cargo.toml b/tcx2csv/Cargo.toml index 47fe9ed..e2154d0 100644 --- a/tcx2csv/Cargo.toml +++ b/tcx2csv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tcx2csv" -version = "0.2.2" +version = "0.3.0" edition = "2021" description = "Parses .TCX files to .JSON and .CSV" license = "Apache-2.0" @@ -11,7 +11,7 @@ include = ["src/**/*", "README.md"] [dependencies] # chrono = "0.4.19" -clap = { version = "3.1.12", features = ["cargo", "color"] } +clap = { version = "4.1.9", features = ["cargo", "wrap_help", "env"] } env_logger = "0.10.0" log = "0.4.16" tcx = "0.9.3" diff --git a/tcx2csv/src/cli.rs b/tcx2csv/src/cli.rs index 95d80d3..0e8c65c 100644 --- a/tcx2csv/src/cli.rs +++ b/tcx2csv/src/cli.rs @@ -1,5 +1,5 @@ //! Contains a single function to build the CLI -use clap::{Arg, ArgMatches, Command}; +use clap::{Arg, ArgAction, ArgMatches, Command}; /// Builds the CLI so the main file doesn't get cluttered. pub fn build() -> ArgMatches { @@ -12,34 +12,35 @@ pub fn build() -> ArgMatches { Arg::new("read") .value_name("FILE(S)") .help("One or more .gpx file(s) to process. Wildcards and multiple_occurrences files (e.g. 2019*.gpx 2020*.gpx) are supported.") - .takes_value(true) + .num_args(1..) .required(true) - .multiple_occurrences(true), + .action(ArgAction::Append) ) .arg( // Hidden debug parameter Arg::new("debug") .short('d') .long("debug") - .multiple_occurrences(true) .help("Output debug information as we go. Supply it twice for trace-level logs.") - .takes_value(false) + .env("FIT_DEBUG") + .num_args(0) + .action(ArgAction::Count) .hide(true), ) .arg( // Don't print any information Arg::new("quiet") .short('q') .long("quiet") - .multiple_occurrences(false) .help("Don't produce any output except errors while working.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) ) .arg( // Don't export detail information Arg::new("detail-off") .short('o') .long("detail-off") - .multiple_occurrences(false) .help("Don't export detailed information from each file parsed.") - .takes_value(false) + .num_args(0) + .action(ArgAction::SetTrue) .requires("summary-file") ) .arg( // Summary file name @@ -47,9 +48,9 @@ pub fn build() -> ArgMatches { .short('s') .value_name("summary output file name") .long("summary-file") - .multiple_occurrences(false) .help("Summary output file name.") - .takes_value(true) + .num_args(1) + .action(ArgAction::Set) ) .get_matches() } diff --git a/tcx2csv/src/main.rs b/tcx2csv/src/main.rs index 5d58e49..185ee68 100644 --- a/tcx2csv/src/main.rs +++ b/tcx2csv/src/main.rs @@ -3,6 +3,7 @@ use std::error::Error; use std::fs::File; use std::io::BufReader; +use clap::parser::ValueSource; use utilities::{TCXActivitiesList, TCXActivity, TCXTrackpointList}; mod cli; @@ -16,17 +17,23 @@ fn run() -> Result<(), Box> { let mut logbuilder = utilities::build_log(&cli_args); logbuilder.target(Target::Stdout).init(); - for argument in cli_args.values_of("read").unwrap_or_default() { + for argument in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::trace!("main::run() -- Arguments: {argument:?}"); } // Find the name of the session output file + let summary_file_name = String::from("tcx-activities.csv"); let summaryfile = cli_args - .value_of("summary-file") - .unwrap_or("tcx-activities.csv"); + .get_one::("summary-file") + .unwrap_or(&summary_file_name) + .as_str(); log::trace!("main::run() -- session output file: {summaryfile}"); // Let the user know if we're writing - if cli_args.is_present("detail-off") { + if cli_args.value_source("detail-off") == Some(ValueSource::CommandLine) { log::debug!("Writing summary file {} only.", &summaryfile); } else { log::debug!("Writing summary and detail files."); @@ -38,14 +45,20 @@ fn run() -> Result<(), Box> { let mut act_list = TCXActivitiesList::new(); - for filename in cli_args.values_of("read").unwrap_or_default() { + for filename in cli_args + .get_many::("read") + .unwrap_or_default() + .map(std::string::String::as_str) + { log::info!("Processing file: {filename}"); let mut tcdb = tcx::read(&mut BufReader::new(File::open(filename)?))?; tcdb.calc_heartrates(); // If -d then export the activity to JSON - if cli_args.is_present("debug") { + if cli_args.value_source("debug") == Some(ValueSource::CommandLine) + || cli_args.value_source("debug") == Some(ValueSource::EnvVariable) + { let outfile = utilities::set_extension(filename, "json") .as_str() .to_owned(); @@ -60,7 +73,7 @@ fn run() -> Result<(), Box> { curr_activities.filename = Some(file_name.clone()); log::trace!("main::run() -- activities summary: {curr_activities:?}"); - if !cli_args.is_present("detail-off") { + if cli_args.value_source("detail-off") != Some(ValueSource::CommandLine) { // Export the activity summary to JSON log::debug!("main::run() -- Writing activity summary for {file_name}"); curr_activities.export_json()?; @@ -76,7 +89,7 @@ fn run() -> Result<(), Box> { } // If we're tracing, export the summary in JSON format - if cli_args.occurrences_of("debug") > 1 { + if cli_args.get_count("debug") > 1 { log::trace!("main::run() -- Exporting summary JSON file."); act_list.export_json(&utilities::set_extension(summaryfile, "json"))?; } diff --git a/utilities/Cargo.toml b/utilities/Cargo.toml index c12083b..13f87f7 100644 --- a/utilities/Cargo.toml +++ b/utilities/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "utilities" -version = "0.3.0" +version = "0.4.0" edition = "2021" description = "Shared utility types and functions for the fitness utilities." license = "Apache-2.0" @@ -19,7 +19,7 @@ env_logger = "0.10.0" log = "0.4.16" csv = "1.1.6" convert_case = "0.6.0" -clap = { version = "3.1.12", features = ["cargo", "color"] } +clap = { version = "4.1.9", features = ["cargo", "wrap_help"] } # Fitness fitparser = "0.5.0" diff --git a/utilities/src/build_logs.rs b/utilities/src/build_logs.rs index 3c4ebd7..3f64303 100644 --- a/utilities/src/build_logs.rs +++ b/utilities/src/build_logs.rs @@ -1,3 +1,4 @@ +use clap::parser::ValueSource; use env_logger::Builder; use log::LevelFilter; @@ -6,14 +7,16 @@ pub fn build_log(cli_args: &clap::ArgMatches) -> Builder { let mut logbuilder = Builder::new(); // Figure out what log level to use. - if cli_args.is_present("quiet") { - logbuilder.filter_level(LevelFilter::Off); - } else { - match cli_args.occurrences_of("debug") { + if cli_args.value_source("quiet") == Some(ValueSource::CommandLine) + || cli_args.value_source("quiet") == Some(ValueSource::EnvVariable) + { + match cli_args.get_count("debug") { 0 => logbuilder.filter_level(LevelFilter::Info), 1 => logbuilder.filter_level(LevelFilter::Debug), _ => logbuilder.filter_level(LevelFilter::Trace), }; + } else { + logbuilder.filter_level(LevelFilter::Off); } // return it