~matthiasbeyer/imag

3e016d901f069eb2c7a772bf71f9920533de6e10 — Matthias Beyer 1 year, 9 months ago 247309b + 29efb33
Merge branch 'imag-bookmark/open' into master

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

M bin/domain/imag-bookmark/src/lib.rs => bin/domain/imag-bookmark/src/lib.rs +90 -0
@@ 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())
}

M bin/domain/imag-bookmark/src/ui.rs => bin/domain/imag-bookmark/src/ui.rs +25 -12
@@ 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),

M imagrc.toml => imagrc.toml +1 -0
@@ 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>`.