diff --git a/bin/domain/imag-todo/Cargo.toml b/bin/domain/imag-todo/Cargo.toml index bbb5667a..3cb37b5a 100644 --- a/bin/domain/imag-todo/Cargo.toml +++ b/bin/domain/imag-todo/Cargo.toml @@ -29,6 +29,7 @@ chrono = "0.4" filters = "0.3" kairos = "0.3" resiter = "0.4.0" +handlebars = "2" libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } @@ -37,6 +38,7 @@ libimagentryedit = { version = "0.10.0", path = "../../../lib/entry/libimagentry libimagtodo = { version = "0.10.0", path = "../../../lib/domain/libimagtodo" } libimagutil = { version = "0.10.0", path = "../../../lib/etc/libimagutil" } libimagentryview = { version = "0.10.0", path = "../../../lib/entry/libimagentryview" } +libimaginteraction = { version = "0.10.0", path = "../../../lib/etc/libimaginteraction" } [dependencies.clap] version = "2.33.0" diff --git a/bin/domain/imag-todo/src/lib.rs b/bin/domain/imag-todo/src/lib.rs index 1ee53439..0943e8c7 100644 --- a/bin/domain/imag-todo/src/lib.rs +++ b/bin/domain/imag-todo/src/lib.rs @@ -43,6 +43,7 @@ extern crate kairos; #[macro_use] extern crate log; #[macro_use] extern crate failure; extern crate resiter; +extern crate handlebars; #[cfg(feature = "import-taskwarrior")] extern crate task_hookrs; @@ -63,7 +64,9 @@ extern crate libimagentryedit; extern crate libimagtodo; extern crate libimagutil; extern crate libimagentryview; +extern crate libimaginteraction; +use std::ops::Deref; use std::io::Write; use std::result::Result as RResult; @@ -77,7 +80,6 @@ use resiter::AndThen; use resiter::IterInnerOkOrElse; use libimagentryedit::edit::Edit; -use libimagentryview::viewer::ViewFromIter; use libimagentryview::viewer::Viewer; use libimagrt::application::ImagApplication; use libimagrt::runtime::Runtime; @@ -88,10 +90,10 @@ use libimagtodo::entry::Todo; use libimagtodo::priority::Priority; use libimagtodo::status::Status; use libimagtodo::store::TodoStore; -use libimagutil::date::datetime_to_string; mod ui; mod import; +mod util; /// Marker enum for implementing ImagApplication on /// @@ -287,9 +289,9 @@ fn list_todos(rt: &Runtime, matcher: &StatusMatcher, show_hidden: bool) -> Resul status = status, first_line = first_line) } else { - let sched = get_dt_str(entry.get_scheduled(), "Not scheduled")?; - let hidden = get_dt_str(entry.get_hidden(), "Not hidden")?; - let due = get_dt_str(entry.get_due(), "No due")?; + let sched = util::get_dt_str(entry.get_scheduled(), "Not scheduled")?; + let hidden = util::get_dt_str(entry.get_hidden(), "Not hidden")?; + let due = util::get_dt_str(entry.get_due(), "No due")?; let priority = entry.get_priority().map_err(E::from)?.map(|p| p.as_str().to_string()) .unwrap_or("No prio".to_string()); @@ -357,41 +359,10 @@ fn list(rt: &Runtime) -> Result<()> { } fn show(rt: &Runtime) -> Result<()> { - #[derive(Default)] - struct TodoShow; - impl Viewer for TodoShow { - - fn view_entry(&self, entry: &Entry, sink: &mut W) -> RResult<(), libimagentryview::error::Error> - where W: Write - { - use libimagentryview::error::Error as E; - - if !entry.is_todo().map_err(E::from)? { - return Err(format_err!("Not a Todo: {}", entry.get_location())).map_err(E::from); - } - - let uuid = entry.get_uuid().map_err(E::from)?; - let status = entry.get_status().map_err(E::from)?; - let status = status.as_str(); - let text = entry.get_content(); - let sched = get_dt_str(entry.get_scheduled(), "Not scheduled")?; - let hidden = get_dt_str(entry.get_hidden(), "Not hidden")?; - let due = get_dt_str(entry.get_due(), "No due")?; - let priority = entry.get_priority().map_err(E::from)?.map(|p| p.as_str().to_string()) - .unwrap_or("No prio".to_string()); - - writeln!(sink, "{uuid}\nStatus: {status}\nPriority: {prio}\nScheduled: {sched}\nHidden: {hidden}\nDue: {due}\n\n{text}", - uuid = uuid, - status = status, - sched = sched, - hidden = hidden, - due = due, - prio = priority, - text = text) - .map_err(Error::from) - .map_err(libimagentryview::error::Error::from) - } - } + let scmd = rt.cli().subcommand_matches("show").unwrap(); + let show_format = util::get_todo_print_format("todo.show_format", rt, &scmd)?; + let out = rt.stdout(); + let mut outlock = out.lock(); rt.ids::()? .ok_or_else(|| err_msg("No ids supplied"))? @@ -402,8 +373,13 @@ fn show(rt: &Runtime) -> Result<()> { .and_then_ok(|e| rt.report_touched(e.get_location()).map_err(Error::from).map(|_| e)) .collect::>>()? .into_iter() - .view::(&mut rt.stdout()) - .map_err(Error::from) + .enumerate() + .map(|(i, elem)| { + let data = util::build_data_object_for_handlebars(i, elem.deref())?; + let s = show_format.render("format", &data)?; + writeln!(outlock, "{}", s).map_err(Error::from) + }) + .collect() } // @@ -437,9 +413,3 @@ fn prio_from_str>(s: S) -> Result { } } -fn get_dt_str(d: Result>, s: &str) -> RResult { - Ok(d.map_err(libimagentryview::error::Error::from)? - .map(|v| datetime_to_string(&v)) - .unwrap_or(s.to_string())) -} - diff --git a/bin/domain/imag-todo/src/ui.rs b/bin/domain/imag-todo/src/ui.rs index 8ed92dba..0b55b2aa 100644 --- a/bin/domain/imag-todo/src/ui.rs +++ b/bin/domain/imag-todo/src/ui.rs @@ -138,6 +138,14 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { ) .subcommand(SubCommand::with_name("show") + .arg(Arg::with_name("format") + .long("format") + .short("F") + .takes_value(true) + .required(false) + .help("Output format string") + ) + .arg(Arg::with_name("todos") .index(1) .takes_value(true) diff --git a/bin/domain/imag-todo/src/util.rs b/bin/domain/imag-todo/src/util.rs new file mode 100644 index 00000000..6e3fd669 --- /dev/null +++ b/bin/domain/imag-todo/src/util.rs @@ -0,0 +1,87 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2019 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::collections::BTreeMap; +use std::result::Result as RResult; + +use failure::Fallible as Result; +use failure::Error; +use failure::err_msg; +use handlebars::Handlebars; +use clap::ArgMatches; +use chrono::NaiveDateTime; +use toml_query::read::TomlValueReadTypeExt; + +use libimagrt::runtime::Runtime; +use libimagstore::store::Entry; +use libimagtodo::entry::Todo; +use libimagutil::date::datetime_to_string; + +pub fn get_dt_str(d: Result>, s: &str) -> RResult { + Ok(d.map_err(libimagentryview::error::Error::from)? + .map(|v| datetime_to_string(&v)) + .unwrap_or(s.to_string())) +} + +pub fn get_todo_print_format(config_value_path: &'static str, rt: &Runtime, scmd: &ArgMatches) -> Result { + let fmt = match scmd.value_of("format").map(String::from) { + Some(s) => Ok(s), + None => { + rt.config() + .ok_or_else(|| err_msg("No configuration file"))? + .read_string(config_value_path) + .map_err(Error::from)? + .ok_or_else(|| format_err!("Configuration '{}' does not exist", config_value_path)) + } + }?; + + let mut hb = Handlebars::new(); + hb.register_template_string("format", fmt)?; + + hb.register_escape_fn(::handlebars::no_escape); + ::libimaginteraction::format::register_all_color_helpers(&mut hb); + ::libimaginteraction::format::register_all_format_helpers(&mut hb); + Ok(hb) +} + +pub fn build_data_object_for_handlebars(i: usize, todo: &Entry) -> Result> { + let mut data = BTreeMap::new(); + + data.insert("i", format!("{}", i)); + let uuid = todo.get_uuid().map_err(Error::from)?.to_string(); + let status = todo.get_status().map_err(Error::from)?; + let status = status.as_str().to_string(); + let text = todo.get_content().to_string(); + let sched = get_dt_str(todo.get_scheduled(), "Not scheduled")?; + let hidden = get_dt_str(todo.get_hidden(), "Not hidden")?; + let due = get_dt_str(todo.get_due(), "No due")?; + let priority = todo.get_priority().map_err(Error::from)?.map(|p| p.as_str().to_string()) + .unwrap_or("No prio".to_string()); + + data.insert("uuid" , uuid); + data.insert("status" , status); + data.insert("text" , text); + data.insert("sched" , sched); + data.insert("hidden" , hidden); + data.insert("due" , due); + data.insert("priority" , priority); + + Ok(data) +} + diff --git a/imagrc.toml b/imagrc.toml index 0bd1c291..7c8947ad 100644 --- a/imagrc.toml +++ b/imagrc.toml @@ -374,3 +374,16 @@ calendars = "/home/user/calendars" # The name of the mail reference collection ref_collection_name = "mail" +[todo] +show_format = """ +{{i}} {{uuid}} + +Status: {{status}} +Priority: {{prio}} +Scheduled: {{sched}} +Hidden: {{hidden}} +Due: {{due}} + +{{text}} +""" +