From 75b1d616d85df31dec917a7c2848a23e448e9f5e Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 12 Dec 2019 22:39:16 +0100 Subject: Rewrite imag-habit to propagate errors to main() instead of exit()ing Signed-off-by: Matthias Beyer --- bin/domain/imag-habit/Cargo.toml | 1 + bin/domain/imag-habit/src/lib.rs | 538 ++++++++++++++++++--------------------- 2 files changed, 242 insertions(+), 297 deletions(-) diff --git a/bin/domain/imag-habit/Cargo.toml b/bin/domain/imag-habit/Cargo.toml index a796c2e6..2e64c2fb 100644 --- a/bin/domain/imag-habit/Cargo.toml +++ b/bin/domain/imag-habit/Cargo.toml @@ -27,6 +27,7 @@ toml-query = "0.9.2" kairos = "0.3.0" prettytable-rs = "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-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) -> 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::>(); + }) + .collect::>>() + .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::>(); - - info!("Done"); + .collect::>>() + .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::>>()?; - // 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::>>()? + .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::() .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::>>() + .map(|_| ()) } else { - fn lister_fn(h: &FileLockEntry) -> Vec { + fn lister_fn(h: &FileLockEntry) -> Result> { 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("")); - 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 = 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::>>()?; 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 { +fn list(rt: &Runtime) -> Result<()> { + fn lister_fn(h: &FileLockEntry) -> Result> { 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(""), 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 = 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::>>()?; 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 { + fn instance_lister_fn(rt: &Runtime, i: &FileLockEntry) -> Result> { 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::>>()?; 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::>(); + .collect::>>() + .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::>>()?; // 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> { - 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(()) } -- cgit v1.2.3