From 29efb337463eb25a19b61557ef431ee09431a406 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 22 Dec 2019 12:43:10 +0100 Subject: Add open command for imag-bookmark Signed-off-by: Matthias Beyer --- bin/domain/imag-bookmark/Cargo.toml | 2 + bin/domain/imag-bookmark/src/lib.rs | 90 +++++++++++++++++++++++++++++++++++++ bin/domain/imag-bookmark/src/ui.rs | 37 ++++++++++----- imagrc.toml | 1 + 4 files changed, 118 insertions(+), 12 deletions(-) diff --git a/bin/domain/imag-bookmark/Cargo.toml b/bin/domain/imag-bookmark/Cargo.toml index c5851236..4462144b 100644 --- a/bin/domain/imag-bookmark/Cargo.toml +++ b/bin/domain/imag-bookmark/Cargo.toml @@ -26,6 +26,8 @@ toml-query = "0.9.2" failure = "0.1.5" resiter = "0.4.0" url = "2" +handlebars = "2" +rayon = "1" libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" } libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" } diff --git a/bin/domain/imag-bookmark/src/lib.rs b/bin/domain/imag-bookmark/src/lib.rs index 6782ed59..21d4bd4a 100644 --- a/bin/domain/imag-bookmark/src/lib.rs +++ b/bin/domain/imag-bookmark/src/lib.rs @@ -42,6 +42,8 @@ extern crate uuid; extern crate toml_query; #[macro_use] extern crate failure; extern crate resiter; +extern crate handlebars; +extern crate rayon; extern crate libimagbookmark; extern crate libimagrt; @@ -51,6 +53,8 @@ extern crate libimagutil; extern crate libimagentryurl; use std::io::Write; +use std::collections::BTreeMap; +use std::process::Command; use failure::Error; use failure::err_msg; @@ -59,10 +63,15 @@ use resiter::AndThen; use resiter::IterInnerOkOrElse; use clap::App; use url::Url; +use handlebars::Handlebars; +use rayon::iter::ParallelIterator; +use rayon::iter::IntoParallelIterator; +use toml_query::read::TomlValueReadExt; use libimagrt::runtime::Runtime; use libimagrt::application::ImagApplication; use libimagstore::iter::get::StoreIdGetIteratorExtension; +use libimagstore::store::FileLockEntry; use libimagbookmark::store::BookmarkStore; use libimagbookmark::bookmark::Bookmark; use libimagentryurl::link::Link; @@ -79,6 +88,7 @@ impl ImagApplication for ImagBookmark { fn run(rt: Runtime) -> Result<()> { match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? { "add" => add(&rt), + "open" => open(&rt), "list" => list(&rt), "remove" => remove(&rt), "find" => find(&rt), @@ -125,6 +135,63 @@ fn add(rt: &Runtime) -> Result<()> { .collect() } +fn open(rt: &Runtime) -> Result<()> { + let scmd = rt.cli().subcommand_matches("open").unwrap(); + let open_command = rt.config() + .map(|value| { + value.read("bookmark.open")? + .ok_or_else(|| err_msg("Configuration missing: 'bookmark.open'"))? + .as_str() + .ok_or_else(|| err_msg("Open command should be a string")) + }) + .or_else(|| Ok(scmd.value_of("opencmd")).transpose()) + .unwrap_or_else(|| Err(err_msg("No open command available in config or on commandline")))?; + + let hb = { + let mut hb = Handlebars::new(); + hb.register_template_string("format", open_command)?; + hb + }; + + let iter = rt.ids::()? + .ok_or_else(|| err_msg("No ids supplied"))? + .into_iter() + .map(Ok) + .into_get_iter(rt.store()) + .map_inner_ok_or_else(|| err_msg("Did not find one entry")); + + if scmd.is_present("openparallel") { + let links = iter + .and_then_ok(|link| rt.report_touched(link.get_location()).map_err(Error::from).map(|_| link)) + .and_then_ok(|link| calculate_command_data(&hb, &link, open_command)) + .collect::>>()?; + + links + .into_par_iter() + .map(|command_rendered| { + Command::new(&command_rendered[0]) // indexing save with check above + .args(&command_rendered[1..]) + .output() + .map_err(Error::from) + }) + .collect::>>() + .map(|_| ()) + } else { + iter.and_then_ok(|link| { + rt.report_touched(link.get_location()).map_err(Error::from).map(|_| link) + }) + .and_then_ok(|link| { + let command_rendered = calculate_command_data(&hb, &link, open_command)?; + Command::new(&command_rendered[0]) // indexing save with check above + .args(&command_rendered[1..]) + .output() + .map_err(Error::from) + }) + .collect::>>() + .map(|_| ()) + } +} + fn list(rt: &Runtime) -> Result<()> { rt.store() .all_bookmarks()? @@ -193,3 +260,26 @@ fn find(rt: &Runtime) -> Result<()> { }) .collect() } + +fn calculate_command_data<'a>(hb: &Handlebars, entry: &FileLockEntry<'a>, open_command: &str) -> Result> { + let url = entry.get_url()? + .ok_or_else(|| format_err!("Failed to retrieve URL for {}", entry.get_location()))? + .into_string(); + + let data = { + let mut data = BTreeMap::new(); + data.insert("url", url); + data + }; + + let command_rendered = hb.render("format", &data)? + .split_whitespace() + .map(String::from) + .collect::>(); + + if command_rendered.len() > 2 { + return Err(format_err!("Command seems not to include URL: '{}'", open_command)); + } + + Ok(command_rendered.into_iter().map(String::from).collect()) +} diff --git a/bin/domain/imag-bookmark/src/ui.rs b/bin/domain/imag-bookmark/src/ui.rs index d1024764..a4016a49 100644 --- a/bin/domain/imag-bookmark/src/ui.rs +++ b/bin/domain/imag-bookmark/src/ui.rs @@ -54,18 +54,30 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .help("Remove these urls, specified as ID")) ) - // .subcommand(SubCommand::with_name("open") - // .about("Open bookmarks (via xdg-open)") - // .version("0.1") - // .arg(Arg::with_name("collection") - // .long("collection") - // .short("c") - // .takes_value(true) - // .required(true) - // .multiple(false) - // .value_name("COLLECTION") - // .help("Select from this collection")) - // ) + .subcommand(SubCommand::with_name("open") + .about("Open bookmarks") + .version("0.1") + .arg(Arg::with_name("ids") + .index(1) + .takes_value(true) + .required(false) + .multiple(true) + .value_name("ID") + .help("open these urls, specified as ID")) + .arg(Arg::with_name("opencmd") + .long("with") + .takes_value(true) + .required(false) + .multiple(false) + .value_name("spec") + .help("Open by executing this command, '{{url}}' will be replaced with the URL of the bookmark. Default is the setting bookmark.open in the configuration file.")) + .arg(Arg::with_name("openparallel") + .long("parallel") + .takes_value(false) + .required(false) + .multiple(false) + .help("Open all links in parallel, don't open sequencially if opening commands blocks.")) + ) .subcommand(SubCommand::with_name("list") .about("List bookmarks, if used in pipe, ignores everything that is not a bookmark") @@ -121,6 +133,7 @@ impl IdPathProvider for PathProvider { match matches.subcommand() { ("add", _) => no_ids_error(), + ("open", Some(subm)) => get_id_paths("ids", subm), ("remove", Some(subm)) => get_id_paths("ids", subm), ("list", Some(subm)) => get_id_paths("ids", subm), ("find", Some(subm)) => get_id_paths("ids", subm), diff --git a/imagrc.toml b/imagrc.toml index 7c8947ad..40a3ac49 100644 --- a/imagrc.toml +++ b/imagrc.toml @@ -273,6 +273,7 @@ timed = "minutely" [bookmark] default_collection = "default" +open = "firefox {{url}}" [view.viewers] # Configure which viewers there are for `imag view in `. -- cgit v1.2.3