diff --git a/bin/core/imag-view/Cargo.toml b/bin/core/imag-view/Cargo.toml index 39b508b1..8bdd122c 100644 --- a/bin/core/imag-view/Cargo.toml +++ b/bin/core/imag-view/Cargo.toml @@ -16,7 +16,11 @@ homepage = "http://imag-pim.org" [dependencies] clap = ">=2.17" log = "0.3" +toml = "0.4" +toml-query = "0.3" version = "2.0.1" +handlebars = "0.29.0" +tempfile = "2.1" libimagstore = { version = "0.5.0", path = "../../../lib/core/libimagstore" } libimagrt = { version = "0.5.0", path = "../../../lib/core/libimagrt" } diff --git a/bin/core/imag-view/src/editor.rs b/bin/core/imag-view/src/editor.rs deleted file mode 100644 index bbd255f7..00000000 --- a/bin/core/imag-view/src/editor.rs +++ /dev/null @@ -1,43 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 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 libimagstore::store::FileLockEntry; -use libimagrt::runtime::Runtime; -use libimagentryview::builtin::editor::EditorView; -use libimagentryview::viewer::Viewer; -use libimagentryview::error::ViewError as VE; - -pub struct Editor<'a> { - rt: &'a Runtime<'a>, - fle: &'a FileLockEntry<'a>, -} - -impl<'a> Editor<'a> { - pub fn new(rt: &'a Runtime, fle: &'a FileLockEntry) -> Editor<'a> { - Editor{ - rt: rt, - fle: fle, - } - } - - pub fn show(self) -> Result<(), VE> { - EditorView::new(self.rt).view_entry(self.fle) - } -} - diff --git a/bin/core/imag-view/src/main.rs b/bin/core/imag-view/src/main.rs index e6b78092..318f4e22 100644 --- a/bin/core/imag-view/src/main.rs +++ b/bin/core/imag-view/src/main.rs @@ -35,25 +35,35 @@ extern crate clap; #[macro_use] extern crate log; #[macro_use] extern crate version; +extern crate handlebars; +extern crate tempfile; +extern crate toml; +extern crate toml_query; extern crate libimagentryview; extern crate libimagerror; extern crate libimagrt; extern crate libimagstore; -use std::process::exit; +use std::collections::BTreeMap; +use std::io::Write; use std::path::PathBuf; +use std::process::Command; +use std::process::exit; + +use handlebars::Handlebars; +use toml_query::read::TomlValueReadExt; +use toml::Value; use libimagrt::setup::generate_runtime_setup; use libimagerror::trace::trace_error_exit; +use libimagerror::trace::MapErrTrace; use libimagentryview::builtin::stdout::StdoutViewer; use libimagentryview::viewer::Viewer; +use libimagentryview::error::ViewError as VE; mod ui; -mod editor; - use ui::build_ui; -use editor::Editor; fn main() { let rt = generate_runtime_setup( "imag-view", @@ -76,34 +86,98 @@ fn main() { } }; - let res = { - match rt.cli().subcommand_matches("view-in") { - None => { - debug!("No commandline call"); - debug!("Assuming to view in cli (stdout)"); + if rt.cli().is_present("in") { + let viewer = rt + .cli() + .value_of("in") + .ok_or_else::(|| "No viewer given".to_owned().into()) + .map_err_trace_exit(1) + .unwrap(); // saved by above call + + let config = rt + .config() + .ok_or_else::(|| "No configuration, cannot continue".to_owned().into()) + .map_err_trace_exit(1) + .unwrap(); + + let query = format!("view.viewers.{}", viewer); + match config.config().read(&query) { + Err(e) => trace_error_exit(&e, 1), + Ok(None) => { + error!("Cannot find '{}' in config", query); + exit(1) }, - Some(s) => { - if s.is_present("view-in-stdout") { - } else if s.is_present("view-in-ui") { - warn!("Viewing in UI is currently not supported, switch to stdout"); - } else if s.is_present("view-in-browser") { - warn!("Viewing in browser is currently not supported, switch to stdout"); - } else if s.is_present("view-in-texteditor") { - if let Err(e) = Editor::new(&rt, &entry).show() { - error!("Cannot view in editor: {}", e); - trace_error_exit(&e, 1); + + Ok(Some(&Value::String(ref viewer_template))) => { + let mut handlebars = Handlebars::new(); + handlebars.register_escape_fn(::handlebars::no_escape); + + let _ = handlebars.register_template_string("template", viewer_template) + .map_err_trace_exit(1) + .unwrap(); + + let file = { + let mut tmpfile = tempfile::NamedTempFile::new() + .map_err_trace_exit(1) + .unwrap(); + if view_header { + let hdr = toml::ser::to_string_pretty(entry.get_header()) + .map_err_trace_exit(1) + .unwrap(); + let _ = tmpfile.write(format!("---\n{}---\n", hdr).as_bytes()) + .map_err_trace_exit(1) + .unwrap(); } - } else if s.is_present("view-in-custom") { - warn!("Viewing in custom is currently not supported, switch to stdout"); + + if view_content { + let _ = tmpfile.write(entry.get_content().as_bytes()) + .map_err_trace_exit(1) + .unwrap(); + } + + tmpfile + }; + + let file_path = file + .path() + .to_str() + .map(String::from) + .ok_or::("Cannot build path".to_owned().into()) + .map_err_trace_exit(1).unwrap(); + + let mut command = { + let mut data = BTreeMap::new(); + data.insert("entry", file_path); + + let call = handlebars.render("template", &data).map_err_trace_exit(1).unwrap(); + let mut elems = call.split_whitespace(); + let command_string = elems + .next() + .ok_or::("No command".to_owned().into()) + .map_err_trace_exit(1) + .unwrap(); + let mut cmd = Command::new(command_string); + + for arg in elems { + cmd.arg(arg); + } + + cmd + }; + + if !command.status().map_err_trace_exit(1).unwrap().success() { + exit(1) } }, - }; - - StdoutViewer::new(view_header, view_content).view_entry(&entry) - }; - - if let Err(e) = res { - trace_error_exit(&e, 1); + Ok(Some(_)) => { + error!("Type error: Expected String at {}, found non-string", query); + exit(1) + }, + } + } else { + let _ = StdoutViewer::new(view_header, view_content) + .view_entry(&entry) + .map_err_trace_exit(1); } } diff --git a/bin/core/imag-view/src/ui.rs b/bin/core/imag-view/src/ui.rs index d47973d4..cde06012 100644 --- a/bin/core/imag-view/src/ui.rs +++ b/bin/core/imag-view/src/ui.rs @@ -17,7 +17,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, ArgGroup, SubCommand}; +use clap::{Arg, App, SubCommand}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -41,56 +41,12 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .required(false) .help("View content")) - .subcommand(SubCommand::with_name("view-in") - .about("View the entry in ...") - .version("0.1") - - .arg(Arg::with_name("view-in-stdout") - .long("stdout") - .short("s") - .takes_value(false) - .required(false) - .help("View by printing to stdout")) - - .arg(Arg::with_name("view-in-ui") - .long("ui") - .short("u") - .takes_value(false) - .required(false) - .help("View by opening own curses-like UI (default)")) - - .arg(Arg::with_name("view-in-browser") - .long("browser") - .short("b") - .takes_value(true) // optional, which browser - .required(false) - .help("View content in $BROWSER (fails if no env variable $BROWSER)") - .value_name("BROWSER")) - - .arg(Arg::with_name("view-in-texteditor") - .long("in-editor") - .short("e") - .takes_value(false) - .required(false) - .help("View content in $EDITOR (can be passed via --editor)")) - - .arg(Arg::with_name("view-in-custom") - .long("custom") - .short("c") - .takes_value(true) // non-optional, call-string - .required(false) - .help("View content in custom program, for example 'libreoffice %e', replace '%e' with entry path") - .value_name("PROGRAM")) - - .group(ArgGroup::with_name("viewer") - .args(&["view-in-stdout", - "view-in-ui", - "view-in-browser", - "view-in-texteditor", - "view-in-custom", - ]) - .required(false)) - ) + .arg(Arg::with_name("in") + .long("in") + .takes_value(true) + .required(false) + .multiple(false) + .help("View content. If no value is given, this fails. Possible viewers are configured via the config file.")) .subcommand(SubCommand::with_name("compile") .about("Compile content to other format for viewing, implies that the entry gets copied to /tmp") diff --git a/doc/src/09020-changelog.md b/doc/src/09020-changelog.md index fa234ed4..79edabf9 100644 --- a/doc/src/09020-changelog.md +++ b/doc/src/09020-changelog.md @@ -22,6 +22,9 @@ This section contains the changelog from the last release to the next release. * `imag-counter` and `libimagcounter` was removed. * `imag-mv` was introduced * `imag-view` uses positional args now + * `imag-view` uses the configuration file now to find the command to call + for viewing the entry. This way one can view the entry in an editor or the + browser or on the toaster. ## 0.4.0