From d109c345c85f3d688755c1315377b7d50cefd4fd Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Dec 2019 10:19:22 +0100 Subject: Transform to error propagation Transform codebase of imag-diary to propagate errors up to the main() function rather than calling exit(). Signed-off-by: Matthias Beyer --- bin/domain/imag-diary/Cargo.toml | 1 + bin/domain/imag-diary/src/create.rs | 117 ++++++++++++++++-------------------- bin/domain/imag-diary/src/delete.rs | 38 ++++-------- bin/domain/imag-diary/src/lib.rs | 50 +++++++-------- bin/domain/imag-diary/src/list.rs | 37 ++++++------ bin/domain/imag-diary/src/util.rs | 4 +- bin/domain/imag-diary/src/view.rs | 51 ++++++---------- 7 files changed, 122 insertions(+), 176 deletions(-) diff --git a/bin/domain/imag-diary/Cargo.toml b/bin/domain/imag-diary/Cargo.toml index e46a36f3..26432644 100644 --- a/bin/domain/imag-diary/Cargo.toml +++ b/bin/domain/imag-diary/Cargo.toml @@ -26,6 +26,7 @@ toml = "0.5.1" toml-query = "0.9.2" itertools = "0.8.0" failure = "0.1.5" +resiter = "0.4.0" libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } diff --git a/bin/domain/imag-diary/src/create.rs b/bin/domain/imag-diary/src/create.rs index 8a3f80a3..c6c4687f 100644 --- a/bin/domain/imag-diary/src/create.rs +++ b/bin/domain/imag-diary/src/create.rs @@ -24,14 +24,11 @@ use chrono::Timelike; use failure::Error; use failure::ResultExt; use failure::err_msg; +use failure::Fallible as Result; use libimagdiary::diary::Diary; use libimagentryedit::edit::Edit; use libimagrt::runtime::Runtime; -use libimagerror::trace::MapErrTrace; -use libimagerror::exit::ExitUnwrap; -use libimagutil::warn_exit::warn_exit; -use libimagutil::debug_result::DebugResult; use libimagutil::debug_option::DebugOption; use libimagstore::store::FileLockEntry; use libimagstore::store::Store; @@ -40,60 +37,46 @@ use crate::util::get_diary_name; use crate::util::get_diary_timed_config; use crate::util::Timed; -pub fn create(rt: &Runtime) { +pub fn create(rt: &Runtime) -> Result<()> { let diaryname = get_diary_name(rt) - .unwrap_or_else( || warn_exit("No diary selected. Use either the configuration file or the commandline option", 1)); + .ok_or_else(|| err_msg("No diary selected. Use either the configuration file or the commandline option"))?; - let mut entry = create_entry(rt.store(), &diaryname, rt); + let mut entry = create_entry(rt.store(), &diaryname, rt)?; + rt.report_touched(entry.get_location())?; - rt.report_touched(entry.get_location()).unwrap_or_exit(); - - let res = if rt.cli().subcommand_matches("create").unwrap().is_present("no-edit") { + if rt.cli().subcommand_matches("create").unwrap().is_present("no-edit") { debug!("Not editing new diary entry"); + info!("Ok!"); Ok(()) } else { debug!("Editing new diary entry"); entry.edit_content(rt).context(err_msg("Diary edit error")).map_err(Error::from) - }; - - res.map_err_trace_exit_unwrap(); - info!("Ok!"); + } } -fn create_entry<'a>(diary: &'a Store, diaryname: &str, rt: &Runtime) -> FileLockEntry<'a> { +fn create_entry<'a>(diary: &'a Store, diaryname: &str, rt: &Runtime) -> Result> { use crate::util::parse_timed_string; let create = rt.cli().subcommand_matches("create").unwrap(); - create.value_of("timed") - .map(|t| parse_timed_string(t, diaryname).map_err_trace_exit_unwrap()) - .map(Some) - .unwrap_or_else(|| { - match get_diary_timed_config(rt, diaryname).map_err_trace_exit_unwrap() { - Some(t) => Some(t), - None => { - warn!("Missing config: 'diary.diaries.{}.timed'", diaryname); - warn!("Assuming 'false'"); - None - } - } - }) - .map(|timed| { - let time = create_id_from_clispec(&create, timed); - diary.new_entry_at(&diaryname, &time) - .context(err_msg("Store write error")) - .map_err(Error::from) - }) - .unwrap_or_else(|| { - debug!("Creating non-timed entry"); - diary.new_entry_today(diaryname) - }) - .map_dbg(|e| format!("Created: {}", e.get_location())) - .map_err_trace_exit_unwrap() + let timed = match create.value_of("timed").map(|t| parse_timed_string(t, diaryname)) { + Some(t) => t.map(Some), + None => get_diary_timed_config(rt, diaryname) + }?; + + if let Some(timed) = timed { + let time = create_id_from_clispec(&create, timed)?; + diary.new_entry_at(&diaryname, &time) + .context(err_msg("Store write error")) + .map_err(Error::from) + } else { + debug!("Creating non-timed entry"); + diary.new_entry_today(diaryname) + } } -fn create_id_from_clispec(create: &ArgMatches, timed_type: Timed) -> NaiveDateTime { +fn create_id_from_clispec(create: &ArgMatches, timed_type: Timed) -> Result { use std::str::FromStr; let dt = Local::now(); @@ -102,71 +85,73 @@ fn create_id_from_clispec(create: &ArgMatches, timed_type: Timed) -> NaiveDateTi match timed_type { Timed::Daily => { debug!("Creating daily-timed entry"); - ndt.with_hour(0) + Ok(ndt.with_hour(0) .unwrap() // safe because hour = 0 is safe .with_minute(0) .unwrap() // safe because minute = 0 is safe .with_second(0) - .unwrap() // safe because second = 0 is safe + .unwrap()) // safe because second = 0 is safe }, Timed::Hourly => { debug!("Creating hourly-timed entry"); - ndt.with_minute(0) + Ok(ndt.with_minute(0) .unwrap() // safe because minute = 0 is safe .with_second(0) - .unwrap() // safe because second = 0 is safe + .unwrap()) // safe because second = 0 is safe }, Timed::Minutely => { let min = create .value_of("minute") .map_dbg(|m| format!("minute = {:?}", m)) - .and_then(|s| { + .map(|s| { FromStr::from_str(s) - .map_err(|_| warn!("Could not parse minute: '{}'", s)) - .ok() + .map_err(Error::from) + .context(format_err!("Could not parse minute: '{}'", s)) + .map_err(Error::from) }) + .transpose()? .unwrap_or_else(|| ndt.minute()); ndt.with_minute(min) - .unwrap_or_else(|| { - error!("Cannot set {} as minute, would yield invalid time!", min); - ::std::process::exit(1) + .ok_or_else(|| { + format_err!("Cannot set {} as minute, would yield invalid time!", min) }) - .with_second(0) - .unwrap() // safe because second = 0 is safe + .map(|ndt| ndt.with_second(0).unwrap()) // safe because second = 0 is safe }, Timed::Secondly => { let min = create .value_of("minute") .map_dbg(|m| format!("minute = {:?}", m)) - .and_then(|s| { + .map(|s| { FromStr::from_str(s) - .map_err(|_| warn!("Could not parse minute: '{}'", s)) - .ok() + .map_err(Error::from) + .context(format_err!("Could not parse minute: '{}'", s)) + .map_err(Error::from) }) + .transpose()? .unwrap_or_else(|| ndt.minute()); let sec = create .value_of("second") .map_dbg(|s| format!("second = {:?}", s)) - .and_then(|s| { + .map(|s| { FromStr::from_str(s) - .map_err(|_| warn!("Could not parse second: '{}'", s)) - .ok() + .map_err(Error::from) + .context(format_err!("Could not parse second: '{}'", s)) + .map_err(Error::from) }) + .transpose()? .unwrap_or_else(|| ndt.second()); ndt.with_minute(min) - .unwrap_or_else(|| { - error!("Cannot set {} as minute, would yield invalid time!", min); - ::std::process::exit(1) - }) + .ok_or_else(|| { + format_err!("Cannot set {} as minute, would yield invalid time!", min) + })? .with_second(sec) - .unwrap_or_else(|| { - error!("Cannot set {} as second, would yield invalid time!", sec); - ::std::process::exit(1) + .ok_or_else(|| { + format_err!("Cannot set {} as second, would yield invalid time!", sec) }) }, } diff --git a/bin/domain/imag-diary/src/delete.rs b/bin/domain/imag-diary/src/delete.rs index 1b254c18..84a2e586 100644 --- a/bin/domain/imag-diary/src/delete.rs +++ b/bin/domain/imag-diary/src/delete.rs @@ -17,26 +17,23 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use std::process::exit; - use chrono::naive::NaiveDateTime as NDT; +use failure::Fallible as Result; +use failure::err_msg; use libimagdiary::diaryid::DiaryId; use libimagrt::runtime::Runtime; use libimagtimeui::datetime::DateTime; use libimagtimeui::parse::Parse; -use libimagutil::warn_exit::warn_exit; use libimagstore::storeid::IntoStoreId; -use libimagerror::trace::MapErrTrace; -use libimagerror::exit::ExitUnwrap; use crate::util::get_diary_name; -pub fn delete(rt: &Runtime) { +pub fn delete(rt: &Runtime) -> Result<()> { use libimaginteraction::ask::ask_bool; let diaryname = get_diary_name(rt) - .unwrap_or_else(|| warn_exit("No diary selected. Use either the configuration file or the commandline option", 1)); + .ok_or_else(|| err_msg("No diary selected. Use either the configuration file or the commandline option"))?; let to_del_location = rt .cli() @@ -46,34 +43,21 @@ pub fn delete(rt: &Runtime) { .map(|dt| { debug!("DateTime = {:?}", dt); dt }) .and_then(DateTime::parse) .map(Into::into) - .ok_or_else(|| warn_exit("Not deleting entries: missing date/time specification", 1)) + .ok_or_else(|| err_msg("Not deleting entries: missing date/time specification")) .and_then(|dt: NDT| DiaryId::from_datetime(diaryname.clone(), dt).into_storeid()) - .and_then(|id| rt.store().retrieve(id)) - .map_err_trace_exit_unwrap() + .and_then(|id| rt.store().retrieve(id))? .get_location() .clone(); - let mut input = rt.stdin().unwrap_or_else(|| { - error!("No input stream. Cannot ask for permission"); - exit(1); - }); - + let mut input = rt.stdin().ok_or_else(|| err_msg("No input stream. Cannot ask for permission"))?; let mut output = rt.stdout(); - if !ask_bool(&format!("Deleting {:?}", to_del_location), Some(true), &mut input, &mut output) - .map_err_trace_exit_unwrap() - { + if !ask_bool(&format!("Deleting {:?}", to_del_location), Some(true), &mut input, &mut output)? { info!("Aborting delete action"); - return; + return Ok(()); } - rt.report_touched(&to_del_location).unwrap_or_exit(); - - rt - .store() - .delete(to_del_location) - .map_err_trace_exit_unwrap(); - - info!("Ok!"); + rt.report_touched(&to_del_location)?; + rt.store().delete(to_del_location) } diff --git a/bin/domain/imag-diary/src/lib.rs b/bin/domain/imag-diary/src/lib.rs index 23629dc9..f87cd927 100644 --- a/bin/domain/imag-diary/src/lib.rs +++ b/bin/domain/imag-diary/src/lib.rs @@ -36,6 +36,7 @@ #[macro_use] extern crate log; #[macro_use] extern crate failure; +extern crate resiter; extern crate clap; extern crate chrono; extern crate toml; @@ -56,11 +57,12 @@ use std::io::Write; use libimagrt::runtime::Runtime; use libimagrt::application::ImagApplication; -use libimagerror::trace::MapErrTrace; use itertools::Itertools; use clap::App; use failure::Fallible as Result; +use failure::err_msg; +use failure::Error; mod create; mod delete; @@ -81,25 +83,21 @@ use crate::view::view; pub enum ImagDiary {} impl ImagApplication for ImagDiary { fn run(rt: Runtime) -> Result<()> { - if let Some(name) = rt.cli().subcommand_name() { - debug!("Call {}", name); - match name { - "diaries" => diaries(&rt), - "create" => create(&rt), - "delete" => delete(&rt), - "list" => list(&rt), - "view" => view(&rt), - other => { - debug!("Unknown command"); - let _ = rt.handle_unknown_subcommand("imag-diary", other, rt.cli()) - .map_err_trace_exit_unwrap() - .code() - .map(::std::process::exit); - }, - } + match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? { + "diaries" => diaries(&rt), + "create" => create(&rt), + "delete" => delete(&rt), + "list" => list(&rt), + "view" => view(&rt), + other => { + debug!("Unknown command"); + if rt.handle_unknown_subcommand("imag-diary", other, rt.cli())?.success() { + Ok(()) + } else { + Err(err_msg("Failed to handle unknown subcommand")) + } + }, } - - Ok(()) } fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> { @@ -119,20 +117,18 @@ impl ImagApplication for ImagDiary { } } -fn diaries(rt: &Runtime) { +fn diaries(rt: &Runtime) -> Result<()> { use libimagdiary::diary::Diary; - use libimagerror::io::ToExitCode; - use libimagerror::exit::ExitUnwrap; - use libimagerror::iter::TraceIterator; let out = rt.stdout(); let mut outlock = out.lock(); rt.store() - .diary_names() - .map_err_trace_exit_unwrap() - .trace_unwrap_exit() + .diary_names()? + .collect::>>()? + .into_iter() .unique() - .for_each(|n| writeln!(outlock, "{}", n).to_exit_code().unwrap_or_exit()) + .map(|n| writeln!(outlock, "{}", n).map_err(Error::from)) + .collect() } diff --git a/bin/domain/imag-diary/src/list.rs b/bin/domain/imag-diary/src/list.rs index 9c4593ec..634b0ff7 100644 --- a/bin/domain/imag-diary/src/list.rs +++ b/bin/domain/imag-diary/src/list.rs @@ -19,33 +19,28 @@ use std::io::Write; +use failure::Fallible as Result; +use failure::err_msg; +use failure::Error; +use resiter::AndThen; + use libimagdiary::diary::Diary; use libimagrt::runtime::Runtime; -use libimagutil::warn_exit::warn_exit; -use libimagerror::trace::MapErrTrace; -use libimagerror::iter::TraceIterator; -use libimagerror::io::ToExitCode; -use libimagerror::exit::ExitUnwrap; use libimagutil::debug_result::*; use libimagdiary::diaryid::DiaryId; use libimagdiary::diaryid::FromStoreId; use libimagstore::storeid::IntoStoreId; -use failure::Fallible as Result; - use crate::util::get_diary_name; -pub fn list(rt: &Runtime) { +pub fn list(rt: &Runtime) -> Result<()> { let diaryname = get_diary_name(rt) - .unwrap_or_else(|| warn_exit("No diary selected. Use either the configuration file or the commandline option", 1)); + .ok_or_else(|| err_msg("No diary selected. Use either the configuration file or the commandline option"))?; let mut ids = Diary::entries(rt.store(), &diaryname) - .map_dbg_str("Ok") - .map_err_trace_exit_unwrap() - .trace_unwrap_exit() - .map(|id| DiaryId::from_storeid(&id)) - .collect::>>() - .map_err_trace_exit_unwrap(); + .map_dbg_str("Ok")? + .and_then_ok(|id| DiaryId::from_storeid(&id)) + .collect::>>()?; ids.sort_by_key(|id| { [id.year() as u32, id.month(), id.day(), id.hour(), id.minute(), id.second()] @@ -53,13 +48,15 @@ pub fn list(rt: &Runtime) { ids.into_iter() .map(IntoStoreId::into_storeid) - .trace_unwrap_exit() - .for_each(|id| { - rt.report_touched(&id).unwrap_or_exit(); + .and_then_ok(|id| { + rt.report_touched(&id)?; if !rt.output_is_pipe() { - writeln!(rt.stdout(), "{}", id).to_exit_code().unwrap_or_exit() + writeln!(rt.stdout(), "{}", id).map_err(Error::from) + } else { + Ok(()) } - }); + }) + .collect() } diff --git a/bin/domain/imag-diary/src/util.rs b/bin/domain/imag-diary/src/util.rs index 3059b6db..2ae01172 100644 --- a/bin/domain/imag-diary/src/util.rs +++ b/bin/domain/imag-diary/src/util.rs @@ -89,8 +89,6 @@ pub fn parse_timed_string(s: &str, diary_name: &str) -> Result { } else if s == "s" || s == "secondly" { Ok(Timed::Secondly) } else { - let s = format!("Cannot parse config: 'diary.diaries.{}.timed = {}'", - diary_name, s); - Err(format_err!("{}", s)) + Err(format_err!("Cannot parse config: 'diary.diaries.{}.timed = {}'", diary_name, s)) } } diff --git a/bin/domain/imag-diary/src/view.rs b/bin/domain/imag-diary/src/view.rs index 9c77691f..c52d274f 100644 --- a/bin/domain/imag-diary/src/view.rs +++ b/bin/domain/imag-diary/src/view.rs @@ -17,47 +17,32 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use failure::Fallible as Result; +use failure::err_msg; +use failure::Error; +use resiter::AndThen; +use resiter::IterInnerOkOrElse; + use libimagdiary::diary::Diary; -use libimagdiary::viewer::DiaryViewer as DV; +use libimagdiary::viewer::DiaryViewer; use libimagrt::runtime::Runtime; -use libimagerror::trace::MapErrTrace; -use libimagerror::iter::TraceIterator; -use libimagerror::exit::ExitUnwrap; -use libimagerror::io::ToExitCode; -use libimagutil::warn_exit::warn_exit; use libimagstore::iter::get::StoreIdGetIteratorExtension; use libimagentryview::viewer::Viewer; -use libimagentryview::error::Error; use crate::util::get_diary_name; -pub fn view(rt: &Runtime) { - let diaryname = get_diary_name(rt).unwrap_or_else(|| warn_exit("No diary name", 1)); - let hdr = rt.cli().subcommand_matches("view").unwrap().is_present("show-header"); - - let entries = Diary::entries(rt.store(), &diaryname) - .map_err_trace_exit_unwrap() - .into_get_iter(rt.store()) - .trace_unwrap_exit() - .map(|e| e.unwrap_or_else(|| { - error!("Failed to fetch entry"); - ::std::process::exit(1) - })); - - let entries = entries.map(|e| { - rt.report_touched(e.get_location()).unwrap_or_exit(); - - e - }); - - let out = rt.stdout(); +pub fn view(rt: &Runtime) -> Result<()> { + let diaryname = get_diary_name(rt).ok_or_else(|| err_msg("No diary name"))?; + let hdr = rt.cli().subcommand_matches("view").unwrap().is_present("show-header"); + let out = rt.stdout(); let mut outlock = out.lock(); + let viewer = DiaryViewer::new(hdr); - if let Err(e) = DV::new(hdr).view_entries(entries, &mut outlock) { - match e { - Error::Io(e) => Err(e).to_exit_code().unwrap_or_exit(), - Error::Other(e) => Err(e).map_err_trace_exit_unwrap() - } - } + Diary::entries(rt.store(), &diaryname)? + .into_get_iter(rt.store()) + .map_inner_ok_or_else(|| err_msg("Did not find one entry")) + .and_then_ok(|e| viewer.view_entry(&e, &mut outlock).map_err(Error::from).map(|_| e)) + .and_then_ok(|e| rt.report_touched(e.get_location()).map_err(Error::from)) + .collect() } -- cgit v1.2.3