summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2019-12-12 10:19:22 +0100
committerMatthias Beyer <mail@beyermatthias.de>2019-12-12 10:19:23 +0100
commitd109c345c85f3d688755c1315377b7d50cefd4fd (patch)
treec4eebd577014b1f6674f28e4ebdde6de92bb9c9d
parent733979609fb07d5ed301035828ae612dc332f09f (diff)
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 <mail@beyermatthias.de>
-rw-r--r--bin/domain/imag-diary/Cargo.toml1
-rw-r--r--bin/domain/imag-diary/src/create.rs117
-rw-r--r--bin/domain/imag-diary/src/delete.rs38
-rw-r--r--bin/domain/imag-diary/src/lib.rs50
-rw-r--r--bin/domain/imag-diary/src/list.rs37
-rw-r--r--bin/domain/imag-diary/src/util.rs4
-rw-r--r--bin/domain/imag-diary/src/view.rs51
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<FileLockEntry<'a>> {
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<NaiveDateTime> {
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::<Result<Vec<String>>>()?
+ .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::<Result<Vec<_>>>()
- .map_err_trace_exit_unwrap();
+ .map_dbg_str("Ok")?
+ .and_then_ok(|id| DiaryId::from_storeid(&id))
+ .collect::<Result<Vec<_>>>()?;
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<Timed> {
} 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()
}