summaryrefslogtreecommitdiffstats
path: root/bin/domain/imag-habit/src
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2019-12-12 22:39:16 +0100
committerMatthias Beyer <mail@beyermatthias.de>2019-12-21 14:52:12 +0100
commit75b1d616d85df31dec917a7c2848a23e448e9f5e (patch)
tree31168baf14695f72d6618cbfc5af7bca3645f829 /bin/domain/imag-habit/src
parent733979609fb07d5ed301035828ae612dc332f09f (diff)
Rewrite imag-habit to propagate errors to main() instead of exit()ing
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Diffstat (limited to 'bin/domain/imag-habit/src')
-rw-r--r--bin/domain/imag-habit/src/lib.rs538
1 files changed, 241 insertions, 297 deletions
diff --git a/bin/domain/imag-habit/src/lib.rs b/bin/domain/imag-habit/src/lib.rs
index 86fa3b3d..c8075dfe 100644
--- a/bin/domain/imag-habit/src/lib.rs
+++ b/bin/domain/imag-habit/src/lib.rs
@@ -39,6 +39,7 @@ extern crate clap;
extern crate toml;
extern crate toml_query;
extern crate kairos;
+extern crate resiter;
extern crate chrono;
extern crate prettytable;
#[macro_use] extern crate failure;
@@ -51,30 +52,31 @@ extern crate libimagutil;
extern crate libimaginteraction;
use std::io::Write;
-use std::process::exit;
use prettytable::Table;
use prettytable::Cell;
use prettytable::Row;
use failure::Error;
use failure::Fallible as Result;
+use failure::err_msg;
+use resiter::AndThen;
+use resiter::FilterMap;
+use resiter::Filter;
+use resiter::IterInnerOkOrElse;
use clap::App;
+use chrono::NaiveDate;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
-use libimagerror::trace::{MapErrTrace, trace_error};
-use libimagerror::iter::TraceIterator;
-use libimagerror::exit::ExitUnwrap;
-use libimagerror::io::ToExitCode;
use libimaghabit::store::HabitStore;
use libimaghabit::habit::builder::HabitBuilder;
use libimaghabit::habit::HabitTemplate;
use libimagstore::store::FileLockEntry;
-use libimagstore::store::Store;
-use libimagstore::storeid::StoreId;
+use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimaginteraction::ask::ask_bool;
use libimagutil::debug_result::DebugResult;
+
mod ui;
/// Marker enum for implementing ImagApplication on
@@ -84,31 +86,23 @@ mod ui;
pub enum ImagHabit {}
impl ImagApplication for ImagHabit {
fn run(rt: Runtime) -> Result<()> {
- rt
- .cli()
- .subcommand_name()
- .map(|name| {
- debug!("Call {}", name);
- match name {
- "create" => create(&rt),
- "delete" => delete(&rt),
- "list" => list(&rt),
- "today" => today(&rt, false),
- "status" => today(&rt, true),
- "show" => show(&rt),
- "done" => done(&rt),
- other => {
- debug!("Unknown command");
- let _ = rt.handle_unknown_subcommand("imag-habit", 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"))? {
+ "create" => create(&rt),
+ "delete" => delete(&rt),
+ "list" => list(&rt),
+ "today" => today(&rt, false),
+ "status" => today(&rt, true),
+ "show" => show(&rt),
+ "done" => done(&rt),
+ other => {
+ debug!("Unknown command");
+ if rt.handle_unknown_subcommand("imag-contact", other, rt.cli())?.success() {
+ Ok(())
+ } else {
+ Err(err_msg("Failed to handle unknown subcommand"))
}
- })
- .unwrap_or_else(|| today(&rt, true));
-
- Ok(())
+ },
+ }
}
fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
@@ -128,7 +122,7 @@ impl ImagApplication for ImagHabit {
}
}
-fn create(rt: &Runtime) {
+fn create(rt: &Runtime) -> Result<()> {
use kairos::parser::parse as kairos_parse;
use kairos::parser::Parsed;
let scmd = rt.cli().subcommand_matches("create").unwrap(); // safe by call from main()
@@ -137,20 +131,16 @@ fn create(rt: &Runtime) {
let comm = scmd.value_of("create-comment").map(String::from).unwrap(); // safe by clap
let date = scmd.value_of("create-date").unwrap(); // safe by clap
- let parsedate = |d, pname| match kairos_parse(d).map_err_trace_exit_unwrap() {
+ let parsedate = |d, pname| match kairos_parse(d)? {
Parsed::TimeType(tt) => tt.calculate()
- .map_dbg(|y| format!("TimeType yielded: '{:?}'", y))
- .map_err_trace_exit_unwrap()
+ .map_dbg(|y| format!("TimeType yielded: '{:?}'", y))?
.get_moment()
.ok_or_else(|| {
- error!("Error: '{}' parameter does not yield a point in time", pname);
- exit(1)
+ format_err!("Error: '{}' parameter does not yield a point in time", pname)
})
- .unwrap() // safe by above
- .date(),
+ .map(|p| p.date()),
_ => {
- error!("Error: '{}' parameter does not yield a point in time", pname);
- exit(1);
+ Err(format_err!("Error: '{}' parameter does not yield a point in time", pname))
},
};
@@ -162,23 +152,23 @@ fn create(rt: &Runtime) {
let hb = HabitBuilder::default()
.with_name(name)
- .with_basedate(parsedate(date, "date"))
+ .with_basedate(parsedate(date, "date")?)
.with_recurspec(recu)
.with_comment(comm);
let hb = if let Some(until) = scmd.value_of("create-until") {
- hb.with_until(parsedate(until, "until"))
+ hb.with_until(parsedate(until, "until")?)
} else {
hb
};
debug!("Builder = {:?}", hb);
- let fle = hb.build(rt.store()).map_err_trace_exit_unwrap();
- rt.report_touched(fle.get_location()).unwrap_or_exit();
+ let fle = hb.build(rt.store())?;
+ rt.report_touched(fle.get_location()).map_err(Error::from)
}
-fn delete(rt: &Runtime) {
+fn delete(rt: &Runtime) -> Result<()> {
use libimaghabit::instance::HabitInstance;
let scmd = rt.cli().subcommand_matches("delete").unwrap(); // safe by call from main()
@@ -186,76 +176,68 @@ fn delete(rt: &Runtime) {
let yes = scmd.is_present("delete-yes");
let delete_instances = scmd.is_present("delete-instances");
- 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();
- let _ = rt
- .store()
- .all_habit_templates()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .map(|sid| (sid.clone(), rt.store().get(sid).map_err_trace_exit_unwrap())) // get the FileLockEntry
- .filter(|&(_, ref habit)| match habit { // filter for name of habit == name we look for
- Some(ref h) => h.habit_name().map_err_trace_exit_unwrap() == name,
- None => false,
+ rt.store()
+ .all_habit_templates()?
+ .and_then_ok(|sid| rt.store().get(sid.clone()).map(|e| e.map(|e| (sid, e)))) // get the FileLockEntry
+ .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
+ .and_then_ok(|(sid, h)| {
+ let filter_result = h.habit_name()? == name;
+ Ok((filter_result, sid, h))
})
- .filter_map(|(a, o)| o.map(|x| (a, x))) // map: (a, Option<b>) -> Option<(a, b)> -> (a, b)
- .map(|(sid, fle)| {
- if delete_instances {
+ .and_then_ok(|(filter, id, fle)| {
+ if !filter {
+ return Ok(())
+ }
+ if delete_instances {
// if this does not succeed, we did something terribly wrong
- let t_name = fle.habit_name().map_err_trace_exit_unwrap();
+ let t_name = fle.habit_name()?;
assert_eq!(t_name, name);
- let get_instance = |iid| rt.store().get(iid).map_err_trace_exit_unwrap();
- let has_template_name = |i: &FileLockEntry| t_name == i.get_template_name().map_err_trace_exit_unwrap();
- let instance_location = |i: FileLockEntry| i.get_location().clone();
- let delete_instance_by_id = |id| {
- let do_delete = |id| rt.store().delete(id).map_err_trace_exit_unwrap();
- if !yes {
- let q = format!("Really delete {}", id);
- if ask_bool(&q, Some(false), &mut input, &mut output)
- .map_err_trace_exit_unwrap()
- {
- do_delete(id);
+ fle.linked_instances()?
+ .and_then_ok(|instance| {
+ let instance = rt.store().get(instance.clone())?.ok_or_else(|| {
+ format_err!("Failed to find instance: {}", instance)
+ })?;
+
+ if instance.get_template_name()? == t_name {
+ if !yes {
+ let q = format!("Really delete {}", id);
+ if ask_bool(&q, Some(false), &mut input, &mut output)? {
+ rt.store().delete(id.clone())
+ } else {
+ Ok(())
+ }
+ } else {
+ rt.store().delete(id.clone())
+ }
+ } else {
+ Ok(())
}
- } else {
- do_delete(id);
- }
- };
-
- let _ = fle
- .linked_instances()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .filter_map(get_instance)
- .filter(has_template_name)
- .map(instance_location)
- .map(delete_instance_by_id)
- .collect::<Vec<_>>();
+ })
+ .collect::<Result<Vec<_>>>()
+ .map(|_| ())?;
}
drop(fle);
- let do_delete_template = |sid| rt.store().delete(sid).map_err_trace_exit_unwrap();
+ let do_delete_template = |sid| rt.store().delete(sid);
if !yes {
- let q = format!("Really delete template {}", sid);
- if ask_bool(&q, Some(false), &mut input, &mut output)
- .map_err_trace_exit_unwrap()
- {
- do_delete_template(sid);
+ let q = format!("Really delete template {}", id);
+ if ask_bool(&q, Some(false), &mut input, &mut output)? {
+ do_delete_template(id)
+ } else {
+ Ok(())
}
} else {
- do_delete_template(sid);
+ do_delete_template(id)
}
})
- .collect::<Vec<_>>();
-
- info!("Done");
+ .collect::<Result<Vec<_>>>()
+ .map(|_| ())
}
// Almost the same as `list()` but with other lister functions and an additional filter for only
@@ -264,7 +246,7 @@ fn delete(rt: &Runtime) {
// if `future` is false, the `rt.cli()` will be checked or a subcommand "today" and the related
// future flag. If it is true, the check will not be performed and it is assumed that `--future`
// was passed.
-fn today(rt: &Runtime, future: bool) {
+fn today(rt: &Runtime, future: bool) -> Result<()> {
use failure::ResultExt;
let (future, show_done) = {
@@ -282,47 +264,39 @@ fn today(rt: &Runtime, future: bool) {
let today = ::chrono::offset::Local::today().naive_local();
let relevant : Vec<_> = { // scope, to have variable non-mutable in outer scope
- let mut relevant : Vec<_> = rt
+ let mut relevant = rt
.store()
- .all_habit_templates()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .filter_map(|id| match rt.store().get(id.clone()) {
- Ok(Some(h)) => Some(h),
- Ok(None) => {
- error!("No habit found for {:?}", id);
- None
- },
- Err(e) => {
- trace_error(&e);
- None
- },
- })
- .filter(|h| {
- let due = h.next_instance_date().map_err_trace_exit_unwrap();
+ .all_habit_templates()?
+ .into_get_iter(rt.store())
+ .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
+ .and_then_ok(|h| {
+ let due = h.next_instance_date()?;
// today or in future
debug!("Checking {due:?} == {today:?} or (future = {fut} && {due:?} > {today:?}",
due = due, today = today, fut = future);
- due.map(|d| d == today || (future && d > today)).unwrap_or(false)
+ let take = due.map(|d| d == today || (future && d > today)).unwrap_or(false);
+ Ok((take, h))
})
- .collect();
+ .filter_ok(|tpl| tpl.0)
+ .and_then_ok(|tpl| tpl.1.next_instance_date().map(|d| d.map(|d| (d, tpl.1))))
+ .filter_map(|e| e.transpose())
+ .collect::<Result<Vec<(NaiveDate, FileLockEntry)>>>()?;
- // unwrap is safe because we filtered above
- relevant.sort_by_key(|h| h.next_instance_date().map_err_trace_exit_unwrap().unwrap());
+ relevant.sort_by_key(|t| t.0);
relevant
};
+ debug!("relevant = {:?}", relevant);
+
let any_today_relevant = show_done || relevant
.iter()
- .filter(|h| {
- let due = h.next_instance_date().map_err_trace_exit_unwrap();
- debug!("Checking: {:?} == {:?}", due, today);
- due.map(|d| d == today).unwrap_or(false) // relevant today
- })
- .count() != 0;
+ .map(|tpl| tpl.1.next_instance_date())
+ .filter_map(|e| e.transpose())
+ .filter_ok(|due| *due == today)
+ .collect::<Result<Vec<_>>>()?
+ .len() != 0;
debug!("Any today relevant = {}", any_today_relevant);
- debug!("relevant = {:?}", relevant);
if !any_today_relevant {
let n = rt
@@ -334,41 +308,43 @@ fn today(rt: &Runtime, future: bool) {
x.parse::<usize>()
.context(format_err!("Cannot parse String '{}' to integer", x))
.map_err(Error::from)
- .map_err_trace_exit_unwrap()
})
- }).unwrap_or(5);
+ }).unwrap_or(Ok(5))?;
info!("No Habits due today.");
info!("Upcoming:");
// list `n` which are relevant in the future.
- relevant.iter().take(n).for_each(|element| {
- let date = element.next_instance_date().map_err_trace_exit_unwrap();
- let name = element.habit_name().map_err_trace_exit_unwrap();
-
- if let Some(date) = date {
- let is_done = element
- .instance_exists_for_date(date)
- .map_err_trace_exit_unwrap();
-
- if show_done || !is_done {
- info!(" * {date}: {name}", date = date, name = name);
+ relevant.iter()
+ .take(n)
+ .map(|(_, element)| {
+ let date = element.next_instance_date()?;
+ let name = element.habit_name()?;
+
+ if let Some(date) = date {
+ let is_done = element.instance_exists_for_date(date)?;
+ if show_done || !is_done {
+ info!(" * {date}: {name}", date = date, name = name);
+ }
}
- }
- });
+
+ Ok(())
+ })
+ .collect::<Result<Vec<_>>>()
+ .map(|_| ())
} else {
- fn lister_fn(h: &FileLockEntry) -> Vec<String> {
+ fn lister_fn(h: &FileLockEntry) -> Result<Vec<String>> {
debug!("Listing: {:?}", h);
- let name = h.habit_name().map_err_trace_exit_unwrap();
- let basedate = h.habit_basedate().map_err_trace_exit_unwrap();
- let recur = h.habit_recur_spec().map_err_trace_exit_unwrap();
- let due = h.next_instance_date().map_err_trace_exit_unwrap()
- .map(date_to_string_helper)
+ let name = h.habit_name()?;
+ let basedate = h.habit_basedate()?;
+ let recur = h.habit_recur_spec()?;
+ let due = h.next_instance_date()?
+ .map(libimagutil::date::date_to_string)
.unwrap_or_else(|| String::from("<finished>"));
- let comm = h.habit_comment().map_err_trace_exit_unwrap();
+ let comm = h.habit_comment()?;
let v = vec![name, basedate, recur, due, comm];
debug!(" -> {:?}", v);
- v
+ Ok(v)
}
let header = ["#", "Name", "Basedate", "Recurr", "Next Due", "Comment"]
@@ -380,63 +356,65 @@ fn today(rt: &Runtime, future: bool) {
table.set_titles(Row::new(header));
let mut empty = true;
+ let mut i = 0;
relevant
.into_iter()
- .filter(|habit| show_done || {
- let instance_exists = habit
+ .filter(|tpl| show_done || {
+ let instance_exists = tpl.1
.next_instance_date()
- .map_err_trace_exit_unwrap()
- .map(|date| {
- let instance_exists = habit
- .instance_exists_for_date(date)
- .map_err_trace_exit_unwrap();
-
- debug!("instance exists for {:?} for {:?} = {:?}",
- habit.get_location().local_display_string(),
- date,
- instance_exists);
-
- instance_exists
+ .and_then(|date| {
+ match date {
+ None => return Ok(false),
+ Some(d) => {
+ let instance_exists = tpl.1.instance_exists_for_date(d)?;
+
+ debug!("instance exists for {:?} for {:?} = {:?}",
+ tpl.1.get_location().local_display_string(),
+ date,
+ instance_exists);
+
+ Ok(instance_exists)
+ }
+ }
})
.unwrap_or(false);
!instance_exists
})
- .enumerate()
- .for_each(|(i, e)| {
+ .map(|(_, e)| {
let mut v = vec![format!("{}", i)];
- let mut list = lister_fn(&e);
+ let mut list : Vec<String> = lister_fn(&e)?;
- {
- rt
- .report_touched(e.get_location())
- .unwrap_or_exit();
- }
+ rt.report_touched(e.get_location())?;
v.append(&mut list);
table.add_row(v.iter().map(|s| Cell::new(s)).collect());
empty = false;
- });
+ i += 1;
+ Ok(())
+ })
+ .collect::<Result<Vec<_>>>()?;
if !empty {
- let _ = table.print(&mut rt.stdout()).to_exit_code().unwrap_or_exit();
+ let _ = table.print(&mut rt.stdout())?;
}
+
+ Ok(())
}
}
-fn list(rt: &Runtime) {
- fn lister_fn(h: &FileLockEntry) -> Vec<String> {
+fn list(rt: &Runtime) -> Result<()> {
+ fn lister_fn(h: &FileLockEntry) -> Result<Vec<String>> {
debug!("Listing: {:?}", h);
- let name = h.habit_name().map_err_trace_exit_unwrap();
- let basedate = h.habit_basedate().map_err_trace_exit_unwrap();
- let recur = h.habit_recur_spec().map_err_trace_exit_unwrap();
- let comm = h.habit_comment().map_err_trace_exit_unwrap();
- let (due, done) = if let Some(date) = h.next_instance_date().map_err_trace_exit_unwrap() {
+ let name = h.habit_name()?;
+ let basedate = h.habit_basedate()?;
+ let recur = h.habit_recur_spec()?;
+ let comm = h.habit_comment()?;
+ let (due, done) = if let Some(date) = h.next_instance_date()? {
let done = h.instance_exists_for_date(date)
.map(|b| if b { "x" } else { "" })
- .map(String::from)
- .map_err_trace_exit_unwrap();
- (date_to_string_helper(date), done)
+ .map(String::from)?;
+ (libimagutil::date::date_to_string(date), done)
} else {
// "finished" as in "the habit is closed"
(String::from("<finished>"), String::from(""))
@@ -444,7 +422,7 @@ fn list(rt: &Runtime) {
let v = vec![name, basedate, recur, comm, due, done];
debug!(" -> {:?}", v);
- v
+ Ok(v)
}
let header = ["#", "Name", "Basedate", "Recurr", "Comment", "Next Due", "Done"]
@@ -454,58 +432,53 @@ fn list(rt: &Runtime) {
let mut empty = true;
let mut table = Table::new();
+ let mut i = 0;
table.set_titles(Row::new(header));
rt
.store()
- .all_habit_templates()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .filter_map(|id| match rt.store().get(id.clone()) {
- Ok(Some(h)) => Some(h),
- Ok(None) => {
- error!("No habit found for {:?}", id);
- None
- },
- Err(e) => {
- trace_error(&e);
- None
- },
+ .all_habit_templates()?
+ .filter_map_ok(|id| match rt.store().get(id.clone()) {
+ Ok(Some(h)) => Some(Ok(h)),
+ Ok(None) => Some(Err(format_err!("No habit found for {:?}", id))),
+ Err(e) => Some(Err(e)),
})
- .enumerate()
- .for_each(|(i, e)| {
+ .and_then_ok(|r| r)
+ .and_then_ok(|e: FileLockEntry| {
let mut v = vec![format!("{}", i)];
- let mut list = lister_fn(&e);
-
- {
- rt.report_touched(e.get_location()).unwrap_or_exit();
- }
+ let mut list : Vec<String> = lister_fn(&e)?;
+ rt.report_touched(e.get_location())?;
v.append(&mut list);
table.add_row(v.iter().map(|s| Cell::new(s)).collect());
empty = false;
- });
+ i += 1;
+ Ok(())
+ })
+ .collect::<Result<Vec<_>>>()?;
if !empty {
- let _ = table.print(&mut rt.stdout()).to_exit_code().unwrap_or_exit();
+ let _ = table.print(&mut rt.stdout())?;
}
+
+ Ok(())
}
-fn show(rt: &Runtime) {
+fn show(rt: &Runtime) -> Result<()> {
let scmd = rt.cli().subcommand_matches("show").unwrap(); // safe by call from main()
let name = scmd
.value_of("show-name")
.map(String::from)
.unwrap(); // safe by clap
- fn instance_lister_fn(rt: &Runtime, i: &FileLockEntry) -> Vec<String> {
+ fn instance_lister_fn(rt: &Runtime, i: &FileLockEntry) -> Result<Vec<String>> {
use libimagutil::date::date_to_string;
use libimaghabit::instance::HabitInstance;
- let date = date_to_string(i.get_date().map_err_trace_exit_unwrap());
- let comm = i.get_comment(rt.store()).map_err_trace_exit_unwrap();
+ let date = date_to_string(i.get_date()?);
+ let comm = i.get_comment(rt.store())?;
- vec![date, comm]
+ Ok(vec![date, comm])
}
let header = ["#", "Date", "Comment"]
@@ -516,19 +489,18 @@ fn show(rt: &Runtime) {
let mut table = Table::new();
table.set_titles(Row::new(header));
- let _ = rt
- .store()
- .all_habit_templates()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .filter_map(|id| get_from_store(rt.store(), id))
- .filter(|h| h.habit_name().map(|n| name == n).map_err_trace_exit_unwrap())
- .enumerate()
- .map(|(i, habit)| {
- let name = habit.habit_name().map_err_trace_exit_unwrap();
- let basedate = habit.habit_basedate().map_err_trace_exit_unwrap();
- let recur = habit.habit_recur_spec().map_err_trace_exit_unwrap();
- let comm = habit.habit_comment().map_err_trace_exit_unwrap();
+ let mut i = 0;
+
+ rt.store()
+ .all_habit_templates()?
+ .into_get_iter(rt.store())
+ .map_inner_ok_or_else(|| err_msg("Did not find one habit template"))
+ .filter_ok(|h| h.habit_name().map(|n| name == n).unwrap_or(false))
+ .and_then_ok(|habit| {
+ let name = habit.habit_name()?;
+ let basedate = habit.habit_basedate()?;
+ let recur = habit.habit_recur_spec()?;
+ let comm = habit.habit_comment()?;
writeln!(rt.stdout(),
"{i} - {name}\nBase : {b},\nRecurrence: {r}\nComment : {c}\n",
@@ -536,48 +508,39 @@ fn show(rt: &Runtime) {
name = name,
b = basedate,
r = recur,
- c = comm)
- .to_exit_code()
- .unwrap_or_exit();
-
- let mut empty = true;
- let iter = habit
- .linked_instances()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .filter_map(|instance_id| {
- debug!("Getting: {:?}", instance_id);
- rt.store().get(instance_id).map_err_trace_exit_unwrap()
- })
- .enumerate();
-
- // We need to drop here because we iterate over instances and in the
- // instance_lister_fn() we call instance.get_comment(), which internally tries to
- // Store::get() the template object.
- // This would fail because the template is already borrowed.
- drop(habit);
+ c = comm)?;
- iter.for_each(|(i, e)| {
- let mut v = vec![format!("{}", i)];
- let mut instances = instance_lister_fn(&rt, &e);
+ let mut j = 0;
- {
- rt.report_touched(e.get_location()).unwrap_or_exit();
- }
+ let mut empty = true;
+ habit.linked_instances()?
+ .into_get_iter(rt.store())
+ .map_inner_ok_or_else(|| err_msg("Did not find one habit template"))
+ .and_then_ok(|e| {
+ let mut v = vec![format!("{}", j)];
+ let mut instances = instance_lister_fn(&rt, &e)?;
+ rt.report_touched(e.get_location())?;
v.append(&mut instances);
table.add_row(v.iter().map(|s| Cell::new(s)).collect());
empty = false;
- });
+ j += 1;
+ Ok(())
+ })
+ .collect::<Result<Vec<_>>>()?;
if !empty {
- let _ = table.print(&mut rt.stdout()).to_exit_code().unwrap_or_exit();
+ let _ = table.print(&mut rt.stdout()).map_err(Error::from);
}
+
+ i += 1;
+ Ok(())
})
- .collect::<Vec<_>>();
+ .collect::<Result<Vec<_>>>()
+ .map(|_| ())
}
-fn done(rt: &Runtime) {
+fn done(rt: &Runtime) -> Result<()> {
let scmd = rt.cli().subcommand_matches("done").unwrap(); // safe by call from main()
let names : Vec<_> = scmd.values_of("done-name").unwrap().map(String::from).collect();
@@ -586,32 +549,33 @@ fn done(rt: &Runtime) {
let relevant : Vec<_> = { // scope, to have variable non-mutable in outer scope
let mut relevant : Vec<_> = rt
.store()
- .all_habit_templates()
- .map_err_trace_exit_unwrap()
- .trace_unwrap_exit()
- .filter_map(|id| get_from_store(rt.store(), id))
- .filter(|h| {
- let due = h.next_instance_date().map_err_trace_exit_unwrap();
- due.map(|d| d <= today || scmd.is_present("allow-future"))
- .unwrap_or(false)
+ .all_habit_templates()?
+ .into_get_iter(rt.store())
+ .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
+ .and_then_ok(|h| {
+ let due = h.next_instance_date()?;
+ let take = due.map(|d| d <= today || scmd.is_present("allow-future")).unwrap_or(false);
+
+ Ok((take, h))
})
- .filter(|h| {
- names.contains(&h.habit_name().map_err_trace_exit_unwrap())
- })
- .collect();
+ .filter_ok(|tpl| tpl.0)
+ .and_then_ok(|tpl| Ok((names.contains(&tpl.1.habit_name()?), tpl.1)))
+ .filter_ok(|tpl| tpl.0)
+ .and_then_ok(|tpl| Ok((tpl.1.next_instance_date()?, tpl.1)))
+ .collect::<Result<Vec<(_, _)>>>()?;
// unwrap is safe because we filtered above
- relevant.sort_by_key(|h| h.next_instance_date().map_err_trace_exit_unwrap().unwrap());
+ relevant.sort_by_key(|tpl| tpl.0);
relevant
};
- for mut r in relevant {
- let next_instance_name = r.habit_name().map_err_trace_exit_unwrap();
- let next_instance_date = r.next_instance_date().map_err_trace_exit_unwrap();
+ for tpl in relevant {
+ let mut r = tpl.1;
+ let next_instance_name = r.habit_name()?;
+ let next_instance_date = r.next_instance_date()?;
if let Some(next) = next_instance_date {
debug!("Creating new instance on {:?}", next);
- r.create_instance_with_date(rt.store(), next)
- .map_err_trace_exit_unwrap();
+ r.create_instance_with_date(rt.store(), next)?;
info!("Done on {date}: {name}",
date = libimagutil::date::date_to_string(next),
@@ -621,30 +585,10 @@ fn done(rt: &Runtime) {
next_instance_name);
}
- {
- rt.report_touched(r.get_location()).unwrap_or_exit();
- }
-
+ rt.report_touched(r.get_location())?;
}
- info!("Done.");
-}
-/// Helper function for `Iterator::filter_map()`ing `all_habit_templates()` and `Store::get` them.
-fn get_from_store(store: &Store, id: StoreId) -> Option<FileLockEntry<'_>> {
- match store.get(id.clone()) {
- Ok(Some(h)) => Some(h),
- Ok(None) => {
- error!("No habit found for {:?}", id);
- None
- },
- Err(e) => {
- trace_error(&e);
- None
- },
- }
-}
-
-fn date_to_string_helper(d: chrono::NaiveDate) -> String {
- libimagutil::date::date_to_string(d)
+ info!("Done.");
+ Ok(())
}