diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2019-12-20 22:59:01 +0100 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2019-12-20 22:59:01 +0100 |
commit | 7542b8c8fe19ede0f06eb23d3fae8887fcb106cb (patch) | |
tree | 2f76fbb15cba3dd5a6b322e8a5b298651f6a81a6 | |
parent | 733979609fb07d5ed301035828ae612dc332f09f (diff) |
Rewrite imag-log to propagate errors to main() instead of exit()ing
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r-- | bin/domain/imag-log/Cargo.toml | 1 | ||||
-rw-r--r-- | bin/domain/imag-log/src/lib.rs | 169 |
2 files changed, 78 insertions, 92 deletions
diff --git a/bin/domain/imag-log/Cargo.toml b/bin/domain/imag-log/Cargo.toml index 5466caab..6bb3eeb2 100644 --- a/bin/domain/imag-log/Cargo.toml +++ b/bin/domain/imag-log/Cargo.toml @@ -27,6 +27,7 @@ is-match = "0.1.0" itertools = "0.8.0" failure = "0.1.5" textwrap = "0.11.0" +resiter = "0.4.0" libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } diff --git a/bin/domain/imag-log/src/lib.rs b/bin/domain/imag-log/src/lib.rs index 338a482d..a6d21427 100644 --- a/bin/domain/imag-log/src/lib.rs +++ b/bin/domain/imag-log/src/lib.rs @@ -42,6 +42,7 @@ extern crate toml_query; extern crate itertools; extern crate failure; extern crate textwrap; +extern crate resiter; extern crate libimaglog; extern crate libimagrt; @@ -51,20 +52,18 @@ extern crate libimagdiary; use std::io::Write; use std::io::Cursor; -use std::result::Result as RResult; use std::str::FromStr; use failure::Error; use failure::err_msg; use failure::Fallible as Result; +use resiter::Map; +use resiter::AndThen; +use resiter::IterInnerOkOrElse; +use resiter::Filter; use libimagrt::application::ImagApplication; use libimagrt::runtime::Runtime; -use libimagerror::trace::MapErrTrace; -use libimagerror::io::ToExitCode; -use libimagerror::exit::ExitUnwrap; -use libimagerror::iter::TraceIterator; -use libimagerror::exit::ExitCode; use libimagdiary::diary::Diary; use libimagdiary::diaryid::DiaryId; use libimaglog::log::Log; @@ -88,37 +87,33 @@ impl ImagApplication for ImagLog { if let Some(scmd) = rt.cli().subcommand_name() { match scmd { "show" => show(&rt), - other => { + other => { debug!("Unknown command"); - let _ = rt.handle_unknown_subcommand("imag-log", other, rt.cli()) - .map_err_trace_exit_unwrap() - .code() - .map(::std::process::exit); + if rt.handle_unknown_subcommand("imag-bookmark", other, rt.cli())?.success() { + Ok(()) + } else { + Err(err_msg("Failed to handle unknown subcommand")) + } }, } } else { let text = get_log_text(&rt); - let diary_name = rt.cli() - .value_of("diaryname") - .map(String::from) - .unwrap_or_else(|| get_diary_name(&rt)); + let diary_name = match rt.cli().value_of("diaryname").map(String::from) { + Some(s) => s, + None => get_diary_name(&rt)?, + }; debug!("Writing to '{}': {}", diary_name, text); - rt - .store() + rt.store() .new_entry_now(&diary_name) - .map(|mut fle| { - fle.make_log_entry().map_err_trace_exit_unwrap(); + .and_then(|mut fle| { + fle.make_log_entry()?; *fle.get_content_mut() = text; - fle + Ok(fle) }) - .map(|fle| rt.report_touched(fle.get_location()).unwrap_or_exit()) - .map_err_trace_exit_unwrap(); - + .and_then(|fle| rt.report_touched(fle.get_location()).map_err(Error::from)) } - - Ok(()) } fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> { @@ -138,7 +133,7 @@ impl ImagApplication for ImagLog { } } -fn show(rt: &Runtime) { +fn show(rt: &Runtime) -> Result<()> { use std::borrow::Cow; use libimagdiary::iter::DiaryEntryIterator; @@ -147,29 +142,33 @@ fn show(rt: &Runtime) { let scmd = rt.cli().subcommand_matches("show").unwrap(); // safed by main() let iters : Vec<DiaryEntryIterator> = match scmd.values_of("show-name") { Some(values) => values - .map(|diary_name| Diary::entries(rt.store(), diary_name).map_err_trace_exit_unwrap()) - .collect(), + .map(|diary_name| Diary::entries(rt.store(), diary_name)) + .collect::<Result<Vec<DiaryEntryIterator>>>(), None => if scmd.is_present("show-all") { debug!("Showing for all diaries"); - rt.store() - .diary_names() - .map_err_trace_exit_unwrap() + let iter = rt.store() + .diary_names()? .map(|diary_name| { - let diary_name = diary_name.map_err_trace_exit_unwrap(); + let diary_name = diary_name?; debug!("Getting entries for Diary: {}", diary_name); - let entries = Diary::entries(rt.store(), &diary_name).map_err_trace_exit_unwrap(); + let entries = Diary::entries(rt.store(), &diary_name)?; let diary_name = Cow::from(diary_name); - (entries, diary_name) + Ok((entries, diary_name)) }) + .collect::<Result<Vec<(DiaryEntryIterator, Cow<str>)>>>()?; + + let iter = iter.into_iter() .unique_by(|tpl| tpl.1.clone()) .map(|tpl| tpl.0) - .collect() + .collect::<Vec<DiaryEntryIterator>>(); + + Ok(iter) } else { // showing default logs - vec![Diary::entries(rt.store(), &get_diary_name(rt)).map_err_trace_exit_unwrap()] + get_diary_name(rt).and_then(|dname| Diary::entries(rt.store(), &dname)).map(|e| vec![e]) } - }; + }?; let mut do_wrap = if scmd.is_present("show-wrap") { Some(80) @@ -179,26 +178,25 @@ fn show(rt: &Runtime) { let do_remove_newlines = scmd.is_present("show-skipnewlines"); if let Some(wrap_value) = scmd.value_of("show-wrap") { - do_wrap = Some(usize::from_str(wrap_value).map_err(Error::from).map_err_trace_exit_unwrap()); + do_wrap = Some(usize::from_str(wrap_value).map_err(Error::from)?); } let mut output = rt.stdout(); - iters.into_iter() + let v = iters.into_iter() .flatten() .into_get_iter(rt.store()) - .trace_unwrap_exit() - .filter_map(|opt| { - if opt.is_none() { - warn!("Failed to retrieve an entry from an existing store id"); - } - - opt - }) - .filter(|e| e.is_log().map_err_trace_exit_unwrap()) - .map(|entry| (entry.diary_id().map_err_trace_exit_unwrap(), entry)) - .sorted_by_key(|tpl| tpl.0.get_date_representation()) - .map(|tpl| { debug!("Found entry: {:?}", tpl.1); tpl }) + .map_inner_ok_or_else(|| err_msg("Did not find one entry")) + .and_then_ok(|e| e.is_log().map(|b| (b, e))) + .filter_ok(|tpl| tpl.0) + .map_ok(|tpl| tpl.1) + .and_then_ok(|entry| entry.diary_id().map(|did| (did.get_date_representation(), did, entry))) + .collect::<Result<Vec<_>>>()?; + + v.into_iter() + .sorted_by_key(|tpl| tpl.0) + .map(|tpl| (tpl.1, tpl.2)) + .inspect(|tpl| debug!("Found entry: {:?}", tpl.1)) .map(|(id, entry)| { if let Some(wrap_limit) = do_wrap { // assume a capacity here: @@ -206,70 +204,57 @@ fn show(rt: &Runtime) { // 10 + 4 + 2 + 2 + 2 + 2 + 6 + 4 = 32 // plus text, which we assume to be 120 characters... lets allocate 256 bytes. let mut buffer = Cursor::new(Vec::with_capacity(256)); - do_write_to(&mut buffer, id, &entry, do_remove_newlines).unwrap_or_exit(); - let buffer = String::from_utf8(buffer.into_inner()) - .map_err(Error::from) - .map_err_trace_exit_unwrap(); + do_write_to(&mut buffer, id, &entry, do_remove_newlines)?; + let buffer = String::from_utf8(buffer.into_inner())?; // now lets wrap - for line in ::textwrap::wrap(&buffer, wrap_limit).iter() { - writeln!(&mut output, "{}", line).to_exit_code()?; - } + ::textwrap::wrap(&buffer, wrap_limit) + .iter() + .map(|line| writeln!(&mut output, "{}", line).map_err(Error::from)) + .collect::<Result<Vec<_>>>()?; } else { - do_write_to(&mut output, id, &entry, do_remove_newlines).unwrap_or_exit(); + do_write_to(&mut output, id, &entry, do_remove_newlines)?; } - rt - .report_touched(entry.get_location()) - .unwrap_or_exit(); - Ok(()) + rt.report_touched(entry.get_location()).map_err(Error::from) }) - .collect::<RResult<Vec<()>, ExitCode>>() - .unwrap_or_exit(); + .collect::<Result<Vec<_>>>() + .map(|_| ()) } -fn get_diary_name(rt: &Runtime) -> String { +fn get_diary_name(rt: &Runtime) -> Result<String> { use toml_query::read::TomlValueReadExt; use toml_query::read::TomlValueReadTypeExt; let cfg = rt .config() - .ok_or_else(|| err_msg("Configuration not present, cannot continue")) - .map_err_trace_exit_unwrap(); + .ok_or_else(|| err_msg("Configuration not present, cannot continue"))?; let current_log = cfg - .read_string("log.default") - .map_err(Error::from) - .map_err_trace_exit_unwrap() - .ok_or_else(|| err_msg("Configuration missing: 'log.default'")) - .map_err_trace_exit_unwrap(); + .read_string("log.default")? + .ok_or_else(|| err_msg("Configuration missing: 'log.default'"))?; if cfg - .read("log.logs") - .map_err(Error::from) - .map_err_trace_exit_unwrap() - .ok_or_else(|| err_msg("Configuration missing: 'log.logs'")) - .map_err_trace_exit_unwrap() + .read("log.logs")? + .ok_or_else(|| err_msg("Configuration missing: 'log.logs'"))? .as_array() - .ok_or_else(|| err_msg("Configuration 'log.logs' is not an Array")) - .map_err_trace_exit_unwrap() + .ok_or_else(|| err_msg("Configuration 'log.logs' is not an Array"))? .iter() .map(|e| if !is_match!(e, &Value::String(_)) { - error!("Configuration 'log.logs' is not an Array<String>!"); - ::std::process::exit(1) + Err(err_msg("Configuration 'log.logs' is not an Array<String>!")) } else { - e + Ok(e) }) - .map(Value::as_str) - .map(Option::unwrap) // safe by map from above - .map(String::from) - .find(|log| log == ¤t_log) + .map_ok(|value| value.as_str().unwrap()) + .map_ok(String::from) + .collect::<Result<Vec<_>>>()? + .iter() + .find(|log| *log == ¤t_log) .is_none() { - error!("'log.logs' does not contain 'log.default'"); - ::std::process::exit(1) + Err(err_msg("'log.logs' does not contain 'log.default'")) } else { - current_log + Ok(current_log) } } @@ -287,7 +272,7 @@ fn get_log_text(rt: &Runtime) -> String { }) } -fn do_write_to<'a>(sink: &mut dyn Write, id: DiaryId, entry: &FileLockEntry<'a>, do_remove_newlines: bool) -> RResult<(), ExitCode> { +fn do_write_to<'a>(sink: &mut dyn Write, id: DiaryId, entry: &FileLockEntry<'a>, do_remove_newlines: bool) -> Result<()> { let text = if do_remove_newlines { entry.get_content().trim_end().replace("\n", "") } else { @@ -303,6 +288,6 @@ fn do_write_to<'a>(sink: &mut dyn Write, id: DiaryId, entry: &FileLockEntry<'a>, H = id.hour(), M = id.minute(), text = text) - .to_exit_code() + .map_err(Error::from) } |