Enhance listing functionality with handlebars templating

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
Matthias Beyer 2019-10-03 13:27:06 +02:00
parent 8dbb2f1590
commit 14dc03f40f
5 changed files with 114 additions and 33 deletions

View file

@ -21,10 +21,11 @@ is-it-maintained-open-issues = { repository = "matthiasbeyer/imag" }
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
log = "0.4" log = "0.4"
failure = "0.1" failure = "0.1"
walkdir = "2.2.8" walkdir = "2.2.8"
vobject = "0.7" vobject = "0.7"
handlebars = "2"
libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" }
libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" } libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" }

View file

@ -39,6 +39,7 @@
extern crate clap; extern crate clap;
extern crate toml_query; extern crate toml_query;
extern crate walkdir; extern crate walkdir;
extern crate handlebars;
#[macro_use] extern crate libimagrt; #[macro_use] extern crate libimagrt;
extern crate libimagcalendar; extern crate libimagcalendar;
@ -163,7 +164,10 @@ fn import(rt: &Runtime) {
fn list(rt: &Runtime) { fn list(rt: &Runtime) {
use util::*; use util::*;
let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe by clap let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe by clap
let list_format = get_event_print_format("calendar.list_format", rt, &scmd)
.map_err_trace_exit_unwrap();
let ref_config = rt.config() let ref_config = rt.config()
.ok_or_else(|| format_err!("No configuration, cannot continue!")) .ok_or_else(|| format_err!("No configuration, cannot continue!"))
.map_err_trace_exit_unwrap() .map_err_trace_exit_unwrap()
@ -173,8 +177,14 @@ fn list(rt: &Runtime) {
.ok_or_else(|| format_err!("Configuration missing: {}", libimagentryref::reference::Config::LOCATION)) .ok_or_else(|| format_err!("Configuration missing: {}", libimagentryref::reference::Config::LOCATION))
.map_err_trace_exit_unwrap(); .map_err_trace_exit_unwrap();
debug!("List format: {:?}", list_format);
debug!("Ref config : {:?}", ref_config);
let event_filter = |pefle: &ParsedEventFLE| true; // TODO: impl filtering let event_filter = |pefle: &ParsedEventFLE| true; // TODO: impl filtering
let mut listed_events = 0;
rt.store() rt.store()
.all_events() .all_events()
.map_err_trace_exit_unwrap() .map_err_trace_exit_unwrap()
@ -186,37 +196,25 @@ fn list(rt: &Runtime) {
.map(|ev| ParsedEventFLE::parse(ev, &ref_config)) .map(|ev| ParsedEventFLE::parse(ev, &ref_config))
.trace_unwrap_exit() .trace_unwrap_exit()
.filter(|e| event_filter(e)) .filter(|e| event_filter(e))
.for_each(|parsed_event| { .for_each(|parsed_entry| {
for event in parsed_event.get_data().events().filter_map(RResult::ok) { parsed_entry
macro_rules! get_data { .get_data()
($t:expr, $text:expr) => { .events()
($t).map(|obj| obj.into_raw()).unwrap_or_else(|| String::from($text)) .filter_map(RResult::ok)
} .filter(event_filter)
} .for_each(|event| {
listed_events = listed_events + 1;
let data = build_data_object_for_handlebars(listed_events, &event);
let summary = get_data!(event.summary(), "<no summary>"); let rendered = list_format
let uid = get_data!(event.uid(), "<no uid>"); .render("format", &data)
let description = get_data!(event.description(), "<no description>"); .map_err(Error::from)
let dtstart = get_data!(event.dtstart(), "<no start date>"); .map_err_trace_exit_unwrap();
let dtend = get_data!(event.dtend(), "<no end date>");
let location = get_data!(event.location(), "<no location>");
let summary_underline = std::iter::repeat('-').take(summary.len()).collect::<String>(); writeln!(rt.stdout(), "{}", rendered).to_exit_code().unwrap_or_exit()
});
writeln!(rt.stdout(), rt.report_touched(parsed_entry.get_entry().get_location()).unwrap_or_exit();
"{summary}\n{summary_underline}\n\n{uid}\n{description}\n{dtstart}\n{dtend}\n{location}\n\n",
summary = summary,
summary_underline = summary_underline,
uid = uid,
description = description,
dtstart = dtstart,
dtend = dtend,
location = location)
.to_exit_code()
.unwrap_or_exit();
}
rt.report_touched(parsed_event.get_entry().get_location()).unwrap_or_exit();
}); });
} }
@ -225,3 +223,4 @@ fn is_not_hidden(entry: &DirEntry) -> bool {
!entry.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false) !entry.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false)
} }

View file

@ -60,6 +60,13 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.subcommand(SubCommand::with_name("list") .subcommand(SubCommand::with_name("list")
.about("List calendar entries") .about("List calendar entries")
.version("0.1") .version("0.1")
.arg(Arg::with_name("format")
.long("format")
.short("F")
.takes_value(true)
.required(false)
.multiple(false)
.help("Override the format used to list one event"))
) )
} }

View file

@ -17,10 +17,18 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// //
use std::collections::BTreeMap;
use clap::ArgMatches;
use vobject::icalendar::ICalendar; use vobject::icalendar::ICalendar;
use vobject::icalendar::Event;
use handlebars::Handlebars;
use failure::Fallible as Result; use failure::Fallible as Result;
use failure::Error; use failure::Error;
use failure::err_msg;
use toml_query::read::TomlValueReadTypeExt;
use libimagrt::runtime::Runtime;
use libimagstore::store::FileLockEntry; use libimagstore::store::FileLockEntry;
use libimagentryref::reference::fassade::RefFassade; use libimagentryref::reference::fassade::RefFassade;
use libimagentryref::reference::Ref; use libimagentryref::reference::Ref;
@ -58,3 +66,54 @@ impl<'a> ParsedEventFLE<'a> {
} }
} }
pub fn get_event_print_format(config_value_path: &'static str, rt: &Runtime, scmd: &ArgMatches)
-> Result<Handlebars>
{
scmd.value_of("format")
.map(String::from)
.map(Ok)
.unwrap_or_else(|| {
rt.config()
.ok_or_else(|| err_msg("No configuration file"))?
.read_string(config_value_path)?
.ok_or_else(|| err_msg("Configuration 'contact.list_format' does not exist"))
})
.and_then(|fmt| {
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<'a>(i: usize, event: &Event<'a>)
-> BTreeMap<&'static str, String>
{
macro_rules! process_opt {
($t:expr, $text:expr) => {
($t).map(|obj| obj.into_raw()).unwrap_or_else(|| String::from($text))
}
}
let mut data = BTreeMap::new();
data.insert("i" , format!("{}", i));
data.insert("dtend" , process_opt!(event.dtend() , "<no dtend>"));
data.insert("dtstart" , process_opt!(event.dtstart() , "<no dtstart>"));
data.insert("dtstamp" , process_opt!(event.dtstamp() , "<no dtstamp>"));
data.insert("uid" , process_opt!(event.uid() , "<no uid>"));
data.insert("description" , process_opt!(event.description() , "<no description>"));
data.insert("summary" , process_opt!(event.summary() , "<no summary>"));
data.insert("url" , process_opt!(event.url() , "<no url>"));
data.insert("location" , process_opt!(event.location() , "<no location>"));
data.insert("class" , process_opt!(event.class() , "<no class>"));
data.insert("categories" , process_opt!(event.categories() , "<no categories>"));
data.insert("transp" , process_opt!(event.transp() , "<no transp>"));
data.insert("rrule" , process_opt!(event.rrule() , "<no rrule>"));
data
}

View file

@ -334,6 +334,21 @@ Email : {{EMAIL}}
Address : {{ADR}} Address : {{ADR}}
""" """
[calendar]
list_format = "{{lpad 5 i}} | {{abbrev 5 uid}} | {{summary}} | {{location}}"
show_format = """
{{i}} - {{uid}}
Summary : {{summary}}
Start : {{dtstart}}
End : {{dtend}}
Url : {{url}}
Location : {{location}}
{{description}}
"""
[log] [log]
logs = ["default"] logs = ["default"]
default = "default" default = "default"