~matthiasbeyer/imag

cadd9f894478b4c5a9c173655e506c5f4794e04a — Matthias Beyer 3 years ago c2d4ec5
Rewrite show functionality of imag-todo

This implementation uses the handlebars crate so that the show format
can be specified in the configuration file.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
M bin/domain/imag-todo/Cargo.toml => bin/domain/imag-todo/Cargo.toml +2 -0
@@ 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"

M bin/domain/imag-todo/src/lib.rs => bin/domain/imag-todo/src/lib.rs +18 -48
@@ 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<W>(&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::<crate::ui::PathProvider>()?
        .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::<Result<Vec<_>>>()?
        .into_iter()
        .view::<TodoShow, _>(&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: AsRef<str>>(s: S) -> Result<Priority> {
    }
}

fn get_dt_str(d: Result<Option<NaiveDateTime>>, s: &str) -> RResult<String, libimagentryview::error::Error> {
    Ok(d.map_err(libimagentryview::error::Error::from)?
       .map(|v| datetime_to_string(&v))
       .unwrap_or(s.to_string()))
}


M bin/domain/imag-todo/src/ui.rs => bin/domain/imag-todo/src/ui.rs +8 -0
@@ 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)

A bin/domain/imag-todo/src/util.rs => bin/domain/imag-todo/src/util.rs +87 -0
@@ 0,0 1,87 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> 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<Option<NaiveDateTime>>, s: &str) -> RResult<String, libimagentryview::error::Error> {
    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<Handlebars> {
    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<BTreeMap<&'static str, String>> {
    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)
}