diff --git a/Cargo.toml b/Cargo.toml index 4e5b4b3..9f2caeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,7 @@ cleanhive = ["nt_hive2"] anyhow = "1.0" binread = "2.2.0" chrono = "0.4" -clap = {version = "4", features = ["derive", "wrap_help", "cargo"] } +clap = {version = "4.5", features = ["derive", "wrap_help", "cargo"] } clap-verbosity-flag = "2.0.0" csv = "1.2.2" encoding_rs = "0.8" diff --git a/src/bin/mactime2/application.rs b/src/bin/mactime2/application.rs index 8fb6cb2..e925a21 100644 --- a/src/bin/mactime2/application.rs +++ b/src/bin/mactime2/application.rs @@ -50,6 +50,7 @@ pub struct Mactime2Application { format: OutputFormat, bodyfile: Input, dst_zone: Tz, + show_headers: bool, strict_mode: bool, } @@ -73,7 +74,11 @@ impl Mactime2Application { Box::new(OldCsvOutput::new(std::io::stdout(), self.dst_zone)) } - OutputFormat::Csv => Box::new(CsvOutput::new(std::io::stdout(), self.dst_zone)), + OutputFormat::Csv => Box::new(CsvOutput::new( + std::io::stdout(), + self.dst_zone, + self.show_headers, + )), OutputFormat::Txt => Box::new(TxtOutput::new(std::io::stdout(), self.dst_zone)), _ => panic!("invalid execution path"), }); @@ -117,6 +122,7 @@ impl From for Mactime2Application { format, bodyfile: cli.input_file, dst_zone: cli.dst_zone.into_tz().unwrap(), + show_headers: cli.show_headers, strict_mode: cli.strict_mode, } } diff --git a/src/bin/mactime2/cli.rs b/src/bin/mactime2/cli.rs index faab3df..3ae6ea0 100644 --- a/src/bin/mactime2/cli.rs +++ b/src/bin/mactime2/cli.rs @@ -1,9 +1,9 @@ +use chrono_tz::Tz; use clap::{Parser, ValueHint}; use clio::Input; use log::LevelFilter; -use chrono_tz::Tz; -use dfir_toolkit::common::{HasVerboseFlag,TzArgument}; +use dfir_toolkit::common::{HasVerboseFlag, TzArgument}; use super::OutputFormat; @@ -13,11 +13,13 @@ const BODYFILE_HELP: &str = #[cfg(not(feature = "gzip"))] const BODYFILE_HELP: &str = "path to input file or '-' for stdin"; -const AFTER_HELP: &str = color_print::cstr!(r##"IMPORTANT +const AFTER_HELP: &str = color_print::cstr!( + r##"IMPORTANT Note that POSIX specifies that all UNIX timestamps are UTC timestamps. It is up to you to ensure that the bodyfile only contains UNIX timestamps that -comply with the POSIX standard."##); +comply with the POSIX standard."## +); /// Replacement for `mactime` #[derive(Parser)] @@ -29,22 +31,40 @@ pub struct Cli { /// output format, if not specified, default value is 'txt' #[clap( + id("format"), short('F'), long("format"), value_enum, - display_order(600) - )] + conflicts_with_all(["json", "csv"]), + display_order(600))] pub(crate) output_format: Option, /// output as CSV instead of TXT. This is a convenience option, which is identical to `--format=csv` - /// and will be removed in a future release. If you specified `--format` and `-d`, the latter will be ignored. - #[clap(short('d'), display_order(610))] - #[arg(group="csv")] + /// and will be removed in a future release. + #[clap( + id("csv"), + short('d'), + display_order(610), + conflicts_with_all(["json", "format"]))] pub(crate) csv_format: bool, + /// display a header line in the CSV output + #[clap( + id("show-headers"), + short('H'), + long("show-headers"), + display_order(615), + conflicts_with("json") + )] + pub(crate) show_headers: bool, + /// output as JSON instead of TXT. This is a convenience option, which is identical to `--format=json` - /// and will be removed in a future release. If you specified `--format` and `-j`, the latter will be ignored. - #[clap(short('j'), display_order(620))] + /// and will be removed in a future release. + #[clap( + id("json"), + short('j'), + display_order(620), + conflicts_with_all(["csv", "format", "show-headers"]))] pub(crate) json_format: bool, /// name of offset of destination timezone (or 'list' to display all possible values diff --git a/src/bin/mactime2/output/csv_output.rs b/src/bin/mactime2/output/csv_output.rs index 7323c31..f7ed6fc 100644 --- a/src/bin/mactime2/output/csv_output.rs +++ b/src/bin/mactime2/output/csv_output.rs @@ -21,12 +21,12 @@ impl CsvOutput where W: Write + Send, { - pub fn new(writer: W, dst_zone: Tz) -> Self { + pub fn new(writer: W, dst_zone: Tz, has_headers: bool) -> Self { Self { dst_zone, writer: WriterBuilder::new() .delimiter(CSV_DELIMITER) - .has_headers(false) + .has_headers(has_headers) .from_writer(writer), } } @@ -108,7 +108,7 @@ mod tests { line: Arc::new(bf_line), }; - let mut output = CsvOutput::new(Cursor::new(vec![]), Tz::UTC); + let mut output = CsvOutput::new(Cursor::new(vec![]), Tz::UTC, false); output.write_line(&unix_ts, &entry).unwrap(); let mut output = BufReader::new(Cursor::new(output.into_writer().into_inner())).lines(); let out_line = output.next().unwrap().unwrap(); @@ -136,7 +136,7 @@ mod tests { line: Arc::new(bf_line), }; - let mut output = CsvOutput::new(Cursor::new(vec![]), tz); + let mut output = CsvOutput::new(Cursor::new(vec![]), tz, false); let delimiter: char = crate::output::CSV_DELIMITER.into(); output.write_line(&unix_ts, &entry).unwrap(); let mut output = BufReader::new(Cursor::new(output.into_writer().into_inner())).lines();