summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2019-12-22 13:56:22 +0100
committerMatthias Beyer <mail@beyermatthias.de>2019-12-22 13:56:22 +0100
commit3e016d901f069eb2c7a772bf71f9920533de6e10 (patch)
tree0b6b8ff77f5a6dd8209f76f113ad8768e94ada2a
parent247309bf4ea2f736f93a55db4c4f4dcb2f5fa6a3 (diff)
parent29efb337463eb25a19b61557ef431ee09431a406 (diff)
downloadimag-3e016d901f069eb2c7a772bf71f9920533de6e10.tar.gz
imag-3e016d901f069eb2c7a772bf71f9920533de6e10.tar.xz
-rw-r--r--bin/domain/imag-bookmark/Cargo.toml2
-rw-r--r--bin/domain/imag-bookmark/src/lib.rs90
-rw-r--r--bin/domain/imag-bookmark/src/ui.rs37
-rw-r--r--imagrc.toml1
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::<crate::ui::PathProvider>()?
+ .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::<Result<Vec<_>>>()?;
+
+ 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::<Result<Vec<_>>>()
+ .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::<Result<Vec<_>>>()
+ .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<Vec<String>> {
+ 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::<Vec<String>>();
+
+ 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 <entry> in <viewer>`.