diff --git a/src/apps/mactime2/application.rs b/src/apps/mactime2/application.rs index 96a83e6..d073d2e 100644 --- a/src/apps/mactime2/application.rs +++ b/src/apps/mactime2/application.rs @@ -1,8 +1,8 @@ -use anyhow::{Result}; use chrono::offset::TimeZone; use chrono::{LocalResult, NaiveDateTime}; use chrono_tz::Tz; use clap::ValueEnum; +use clio::Input; use super::bodyfile::{BodyfileDecoder, BodyfileSorter, BodyfileReader}; use super::cli::Cli; @@ -32,7 +32,7 @@ pub enum OutputFormat { //#[derive(Builder)] pub struct Mactime2Application { format: OutputFormat, - bodyfile: Option, + bodyfile: Input, src_zone: Tz, dst_zone: Tz, strict_mode: bool, @@ -60,13 +60,13 @@ impl Mactime2Application { } } - pub fn run(&self) -> Result<()> { + pub fn run(&self) -> anyhow::Result<()> { let options = RunOptions { strict_mode: self.strict_mode, src_zone: self.src_zone, }; - let mut reader = >::from(&self.bodyfile)?; + let mut reader = >::from(self.bodyfile.clone())?; let mut decoder = BodyfileDecoder::with_receiver(reader.get_receiver(), options); let mut sorter = self.create_sorter(&mut decoder); sorter.run(); @@ -112,7 +112,7 @@ impl From for Mactime2Application { Self { format, - bodyfile: Some(cli.input_file), + bodyfile: cli.input_file, src_zone: cli .src_zone .map(|tz| tz.parse().unwrap()) @@ -125,15 +125,3 @@ impl From for Mactime2Application { } } } - -impl Default for Mactime2Application { - fn default() -> Self { - Self { - format: OutputFormat::CSV, - bodyfile: None, - src_zone: Tz::UTC, - dst_zone: Tz::UTC, - strict_mode: false, - } - } -} diff --git a/src/apps/mactime2/cli.rs b/src/apps/mactime2/cli.rs index 281ee19..5d9a47a 100644 --- a/src/apps/mactime2/cli.rs +++ b/src/apps/mactime2/cli.rs @@ -1,55 +1,66 @@ +use clap::{Parser, ValueHint}; +use clio::Input; +use log::LevelFilter; -use clap::Parser; +use crate::common::HasVerboseFlag; use super::OutputFormat; #[cfg(feature = "gzip")] -const BODYFILE_HELP: &str = "path to input file or '-' for stdin (files ending with .gz will be treated as being gzipped)"; +const BODYFILE_HELP: &str = + "path to input file or '-' for stdin (files ending with .gz will be treated as being gzipped)"; #[cfg(not(feature = "gzip"))] const BODYFILE_HELP: &str = "path to input file or '-' for stdin"; #[derive(Parser)] #[clap(name="mactime2", author, version, about, long_about = None)] pub struct Cli { - #[clap(short('b'), default_value="-", help=BODYFILE_HELP, display_order(100))] - pub(crate) input_file: String, + #[clap(short('b'), num_args=1, value_parser, value_hint=ValueHint::FilePath, default_value="-", help=BODYFILE_HELP, display_order(100))] + pub(crate) input_file: Input, /// output format, if not specified, default value is 'txt' - #[clap(short('F'), long("format"), value_enum, display_order(600))] + #[clap( + short('F'), + num_args = 1, + long("format"), + value_enum, + display_order(600) + )] pub(crate) output_format: Option, /// output as CSV instead of TXT. This is a conveniance 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))] + #[clap(short('d'), num_args = 0, display_order(610))] pub(crate) csv_format: bool, /// output as JSON instead of TXT. This is a conveniance 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))] + #[clap(short('j'), num_args = 0, display_order(620))] pub(crate) json_format: bool, /// name of offset of source timezone (or 'list' to display all possible values - #[clap(short('f'), long("from-timezone"), display_order(300))] + #[clap(short('f'), num_args = 1, long("from-timezone"), display_order(300))] pub(crate) src_zone: Option, /// name of offset of destination timezone (or 'list' to display all possible values - #[clap(short('t'), long("to-timezone"), display_order(400))] + #[clap(short('t'), num_args = 1, long("to-timezone"), display_order(400))] pub(crate) dst_zone: Option, // /// convert only, but do not sort // #[clap(short('c'), long("convert-only"), display_order(450))] // pub(crate) dont_sort: bool, - /// strict mode: do not only warn, but abort if an error occurs - #[clap(long("strict"), display_order(500))] + #[clap(long("strict"), num_args = 0, display_order(500))] pub(crate) strict_mode: bool, #[clap(flatten)] pub(crate) verbose: clap_verbosity_flag::Verbosity, +} - /// print help in markdown format - #[arg(long, hide = true, exclusive=true)] - pub markdown_help: bool, +impl HasVerboseFlag for Cli { + fn log_level_filter(&self) -> LevelFilter { + self.verbose.log_level_filter() + } } impl Cli { @@ -64,4 +75,4 @@ impl Cli { pub fn dst_zone(&self) -> &Option { &self.dst_zone } -} \ No newline at end of file +} diff --git a/src/apps/mactime2/stream/stream_reader.rs b/src/apps/mactime2/stream/stream_reader.rs index e45581c..da3a0d6 100644 --- a/src/apps/mactime2/stream/stream_reader.rs +++ b/src/apps/mactime2/stream/stream_reader.rs @@ -1,10 +1,10 @@ use std::{ - io::stdin, sync::mpsc::{self, Receiver, Sender}, thread::{self, JoinHandle}, }; use anyhow::Result; +use clio::Input; use crate::apps::mactime2::{stream::*, filter::{Joinable, Provider}}; @@ -12,17 +12,12 @@ pub(crate) trait StreamReader: Sized + StreamWorker + Joinable + Pro where T: Send + 'static, { - fn from(filename: &Option) -> Result { + fn from(input: Input) -> Result { let (tx, rx): (Sender, Receiver) = mpsc::channel(); - let worker = match StreamSource::from(filename)? { - StreamSource::Stdin => thread::spawn(move || { - >::worker(stdin(), tx); - }), - StreamSource::File(f) => thread::spawn(move || { - >::worker(f, tx); - }), - }; + let worker = thread::spawn(move || { + >::worker(StreamSource::from(input), tx); + }); Ok(>::new(worker, rx)) } diff --git a/src/apps/mactime2/stream/stream_source.rs b/src/apps/mactime2/stream/stream_source.rs index 9d14e74..69ba1b4 100644 --- a/src/apps/mactime2/stream/stream_source.rs +++ b/src/apps/mactime2/stream/stream_source.rs @@ -1,41 +1,23 @@ -use std::{io::{Read, BufReader}, fs::File}; -use anyhow::Result; +use std::io::Read; +use clio::Input; #[cfg(feature = "gzip")] use flate2::read::GzDecoder; +pub(crate) struct StreamSource(Box); -pub (crate) enum StreamSource { - Stdin, - File(Box), -} - -impl StreamSource { - pub fn from(filename: &Option) -> Result { - match filename { - None => Ok(StreamSource::Stdin), - Some(filename) => { - if filename == "-" { Ok(StreamSource::Stdin) } - else { - let file = BufReader::new(File::open(filename)?); - - #[cfg(not(feature = "gzip"))] - let reader: Box = Box::new(file); - - #[cfg(feature = "gzip")] - let reader = Self::open_gzip(filename, file); - - Ok(StreamSource::File(reader)) - } - } +impl From for StreamSource { + fn from(input: Input) -> Self { + #[cfg(feature = "gzip")] + if input.path().ends_with(".gz") { + return Self(Box::new(GzDecoder::new(input))); } + + Self(Box::new(input)) } +} - #[cfg(feature = "gzip")] - fn open_gzip(filename: &str, file: R) -> Box { - if filename.ends_with(".gz") { - Box::new(GzDecoder::new(file)) - } else { - Box::new(file) - } +impl Read for StreamSource { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.0.read(buf) } } \ No newline at end of file diff --git a/src/bin/mactime2/main.rs b/src/bin/mactime2/main.rs index 05059ab..ea314c0 100644 --- a/src/bin/mactime2/main.rs +++ b/src/bin/mactime2/main.rs @@ -1,23 +1,12 @@ use anyhow::Result; -use clap::Parser; -use simplelog::{TermLogger, Config, TerminalMode, ColorChoice}; use chrono_tz::TZ_VARIANTS; +use dfir_toolkit::common::FancyParser; use dfir_toolkit::apps::mactime2::Cli; use dfir_toolkit::apps::mactime2::Mactime2Application; fn main() -> Result<()> { - if std::env::args().any(|a| &a == "--markdown-help") { - clap_markdown::print_help_markdown::(); - return Ok(()); - } - let cli = Cli::parse(); - - let _ = TermLogger::init( - cli.verbose().log_level_filter(), - Config::default(), - TerminalMode::Stderr, - ColorChoice::Auto); + let cli: Cli = Cli::parse_cli(); match cli.src_zone().as_deref() { Some("list") => {display_zones(); return Ok(());}