diff options
author | gennyble <gen@nyble.dev> | 2023-08-25 17:57:51 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2023-08-25 17:57:51 -0500 |
commit | 85052baf6ff0ce914ff44435cb8ba3edb8725947 (patch) | |
tree | 3812b173c1035a4090a110bf6dfb14fbc5afa065 /src | |
parent | d1c2d05fdb38c44bc5e92a53cc61183dc2982c7b (diff) | |
download | whenwasit-85052baf6ff0ce914ff44435cb8ba3edb8725947.tar.gz whenwasit-85052baf6ff0ce914ff44435cb8ba3edb8725947.zip |
add quiet flag, error handling
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 145 |
1 files changed, 109 insertions, 36 deletions
diff --git a/src/main.rs b/src/main.rs index 0d560e6..a7c9df7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::{ fs::{File, Metadata}, io::{BufRead, BufReader}, @@ -9,6 +10,7 @@ use getopts::Options; struct Context { root: Utf8PathBuf, + quiet: bool, ignores: Vec<String>, } @@ -16,18 +18,30 @@ impl Context { pub fn new(root: Utf8PathBuf) -> Self { Self { root, + quiet: false, ignores: vec![], } } + /// Reads the ignore file. Errors are fail and exit the process. pub fn ignore_file(&mut self, ignore_path: Utf8PathBuf) { - let file = File::open(ignore_path).unwrap(); - let mut bufread = BufReader::new(file); + let mut bufread = match File::open(&ignore_path) { + Ok(f) => BufReader::new(f), + Err(e) => { + self.print_err(format!("{ignore_path}: {e}")); + std::process::exit(1); + } + }; let mut line = String::new(); loop { - if bufread.read_line(&mut line).unwrap() == 0 { - break; + match bufread.read_line(&mut line) { + Ok(0) => break, + Ok(_) => (), + Err(e) => { + self.print_err(format!("{ignore_path}: {e}")); + std::process::exit(1); + } } if line.starts_with("\\#") || line.starts_with("/") { @@ -56,6 +70,18 @@ impl Context { Err(_) => false, } } + + pub fn print_err<M: fmt::Display>(&self, message: M) { + if !self.quiet { + eprintln!("{message}") + } + } +} + +fn print_usage(opts: &Options) { + let breif = "Usage: whenwasit [options] PATH"; + + println!("{}", opts.usage(&breif)); } fn main() { @@ -63,6 +89,7 @@ fn main() { #[rustfmt::skip] opts.optopt("", "ignore", "file of paths to ignore, one per line", "PATH"); opts.optflag("h", "help", "print this help thing"); + opts.optflag("q", "quiet", "don't print errors to STDERR"); let matches = match opts.parse(std::env::args()) { Ok(m) => m, @@ -73,20 +100,21 @@ fn main() { }; if matches.opt_present("h") { - println!("{}", opts.usage("Usage: whenwasit [options] PATH")); + print_usage(&opts); return; } let root = match matches.free.get(1) { None => { - println!("expected path"); - println!("{}", opts.usage("Usage: whenwasit [options] PATH")); + println!("ERROR No path provided\n"); + print_usage(&opts); return; } Some(p) => Utf8PathBuf::from(p), }; let mut context = Context::new(root); + context.quiet = matches.opt_present("quiet"); if let Some(ignore) = matches.opt_str("ignore") { let ignore = Utf8PathBuf::from(ignore); @@ -101,44 +129,85 @@ fn main() { /// /// Does two passes: First pass prints files. Second pass recurs, printing directories. fn process(ctx: &Context, path: &Utf8Path) { - //TODO: do not panic, please - let this_meta = std::fs::metadata(&path).unwrap(); - let this_times = Times::metadata(&this_meta); - row(&ctx.root, &path, &this_times); - - for entry in path.read_dir_utf8().unwrap() { - let entry = entry.unwrap(); - - match entry.file_type() { - Err(_) => panic!(), - Ok(ft) if ft.is_file() => { - let path = entry.path(); - if ctx.is_file_ignored(path) { + match std::fs::metadata(&path) { + Err(e) => { + ctx.print_err(format!("{path}: {e}")); + return; + } + Ok(meta) => { + let this_times = Times::metadata(&meta); + row(&ctx.root, &path, &this_times); + } + } + + let readdir = match path.read_dir_utf8() { + Err(e) => { + ctx.print_err(format!("{path}: {e}")); + return; + } + Ok(read) => read, + }; + + // 1st loop - print details about every file + for entry in readdir { + let entry = match entry { + Ok(e) => e, + Err(err) => { + ctx.print_err(format!("{path}: failed to read dir entry: {err}")); + continue; + } + }; + + let entry_path = entry.path(); + + match entry.metadata() { + Err(err) => { + ctx.print_err(format!("{entry_path}: {err}")); + continue; + } + Ok(meta) => { + if !meta.is_file() || ctx.is_file_ignored(entry_path) { continue; } - let meta = entry.metadata().unwrap(); let times = Times::metadata(&meta); - row(&ctx.root, path, ×) + row(&ctx.root, entry_path, ×) } - Ok(_) => {} - } + }; } - for entry in path.read_dir_utf8().unwrap() { - let entry = entry.unwrap(); + let readdir = match path.read_dir_utf8() { + Err(e) => { + ctx.print_err(format!("{path}: {e}")); + return; + } + Ok(read) => read, + }; - match entry.file_type() { - Err(_) => panic!(), - Ok(ft) if ft.is_dir() => { - let path = entry.path(); - if ctx.is_file_ignored(path) { + // 2nd loop - run resursivly on directories + for entry in readdir { + let entry = match entry { + Ok(e) => e, + Err(err) => { + ctx.print_err(format!("{path}: failed to read dir entry: {err}")); + continue; + } + }; + + let entry_path = entry.path(); + + match entry.metadata() { + Err(err) => { + ctx.print_err(format!("{entry_path}: {err}")); + continue; + } + Ok(meta) => { + if !meta.is_dir() || ctx.is_file_ignored(entry_path) { continue; } - process(ctx, entry.path()) + process(ctx, entry_path) } - Ok(_) => {} } } } @@ -149,8 +218,6 @@ fn row<P: AsRef<Utf8Path>>(root: &Utf8Path, path: P, times: &Times) { .strip_prefix(root) .expect("row wasn't relative to the root; what happened?"); - /// Returns an owned string with the duration formatted as seconds, or a - /// borrowed empty string fn time_string(time: Option<Duration>) -> String { time.map(|d| d.as_secs().to_string()).unwrap_or_default() } @@ -214,7 +281,13 @@ impl Times { Self::from_epoch(self.accessed.as_ref()) } + /// SystemTime relative to the Unix Epoch. fn from_epoch(maybe_time: Option<&SystemTime>) -> Option<Duration> { - maybe_time.map(|st| st.duration_since(SystemTime::UNIX_EPOCH).unwrap()) + // As far as I know, a SystemTime can not be below UNIX_EPOCH. This + // unwrap should never panic, but we fal to a duration of 0 just in case + maybe_time.map(|st| { + st.duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or(Duration::from_secs(0)) + }) } } |