~matthiasbeyer/imag

cc6a833ab69c8481daa9794c3fb152acf0a22721 — Matthias Beyer 1 year, 5 months ago e3db947 + a3eccc0
Merge branch 'noexit' into master

This merge includes the first batch of "no-exit()-call" patches.

With this patchset, we do not exit() anywhere in the core binaries
anymore, but propagate errors up to the main() function, where they are
returned.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
M bin/core/imag-annotate/src/lib.rs => bin/core/imag-annotate/src/lib.rs +82 -119
@@ 53,17 53,15 @@ use std::io::Write;

use failure::Error;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;
use toml_query::read::TomlValueReadTypeExt;
use clap::App;

use libimagentryannotation::annotateable::*;
use libimagentryannotation::annotation_fetcher::*;
use libimagentryedit::edit::*;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagerror::errors::ErrorMsg as EM;
use libimagerror::iter::TraceIterator;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagstore::store::FileLockEntry;


@@ 75,22 73,19 @@ mod ui;
pub enum ImagAnnotate {}
impl ImagApplication for ImagAnnotate {
    fn run(rt: Runtime) -> Result<()> {
        if let Some(name) = rt.cli().subcommand_name() {
            match name {
                "add"    => add(&rt),
                "remove" => remove(&rt),
                "list"   => list(&rt),
                other    => {
                    debug!("Unknown command");
                    let _ = rt.handle_unknown_subcommand("imag-annotation", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .map(::std::process::exit);
                },
            }
        match rt.cli().subcommand_name().ok_or_else(|| err_msg("No command called"))? {
            "add"    => add(&rt),
            "remove" => remove(&rt),
            "list"   => list(&rt),
            other    => {
                debug!("Unknown command");
                if rt.handle_unknown_subcommand("imag-annotation", other, rt.cli())?.success() {
                    Ok(())
                } else {
                    Err(err_msg("Failed to handle unknown subcommand"))
                }
            },
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 110,149 105,119 @@ impl ImagApplication for ImagAnnotate {
    }
}

fn add(rt: &Runtime) {
fn add(rt: &Runtime) -> Result<()> {
    let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main()
    let mut ids = rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No StoreId supplied");
            ::std::process::exit(1);
        })
        .context("No StoreId supplied")?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter();

    if let Some(first) = ids.next() {
        let mut annotation = rt.store()
            .get(first.clone())
            .map_err_trace_exit_unwrap()
            .ok_or_else(|| EM::EntryNotFound(first.local_display_string()))
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
            .annotate(rt.store())
            .map_err_trace_exit_unwrap();
            .get(first.clone())?
            .ok_or_else(|| EM::EntryNotFound(first.local_display_string()))?
            .annotate(rt.store())?;

        annotation.edit_content(&rt).map_err_trace_exit_unwrap();
        annotation.edit_content(&rt)?;

        for id in ids {
            let mut entry = rt.store().get(id.clone())
                .map_err_trace_exit_unwrap()
                .ok_or_else(|| format_err!("Not found: {}", id.local_display_string()))
                .map_err_trace_exit_unwrap();
            let mut entry = rt.store().get(id.clone())?
                .ok_or_else(|| format_err!("Not found: {}", id.local_display_string()))?;

            entry.add_link(&mut annotation).map_err_trace_exit_unwrap();
            entry.add_link(&mut annotation)?;
        }

        if !scmd.is_present("dont-print-name") {
            if let Some(annotation_id) = annotation
                .get_header()
                .read_string("annotation.name")
                .map_err(Error::from)
                .map_err_trace_exit_unwrap()
                .read_string("annotation.name")?
            {
                writeln!(rt.stdout(), "Name of the annotation: {}", annotation_id)
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(rt.stdout(), "Name of the annotation: {}", annotation_id)?;
            } else {
                error!("Unnamed annotation: {:?}", annotation.get_location());
                error!("This is most likely a BUG, please report!");
                Err(format_err!("Unnamed annotation: {:?}", annotation.get_location()))
                    .context("This is most likely a BUG, please report!")?;
            }
        }
    } else {
        debug!("No entries to annotate");
    }

    Ok(())
}

fn remove(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("remove").unwrap(); // safed by main()
fn remove(rt: &Runtime) -> Result<()> {
    let scmd            = rt.cli().subcommand_matches("remove").unwrap(); // safed by main()
    let annotation_name = scmd.value_of("annotation_name").unwrap(); // safed by clap
    let delete = scmd.is_present("delete-annotation");
    let ids = rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .into_iter();

    ids.for_each(|id| {
        let mut entry = rt.store()
            .get(id.clone())
            .map_err_trace_exit_unwrap()
            .ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
            .map_err(Error::from)
            .map_err_trace_exit_unwrap();

        let annotation = entry
            .denotate(rt.store(), annotation_name)
            .map_err_trace_exit_unwrap();

        if delete {
            debug!("Deleting annotation object");
            if let Some(an) = annotation {
                let loc = an.get_location().clone();
                drop(an);

                rt
                    .store()
                    .delete(loc)
                    .map_err_trace_exit_unwrap();
    let delete          = scmd.is_present("delete-annotation");

    rt.ids::<crate::ui::PathProvider>()
        .context("No ids supplied")?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .map(|id| {
            let mut entry = rt.store()
                .get(id.clone())?
                .ok_or_else(|| EM::EntryNotFound(id.local_display_string()))?;

            let annotation = entry.denotate(rt.store(), annotation_name)?;

            if delete {
                debug!("Deleting annotation object");
                if let Some(an) = annotation {
                    let loc = an.get_location().clone();
                    drop(an);

                    rt.store().delete(loc)?;
                } else {
                    warn!("Not having annotation object, cannot delete!");
                }
            } else {
                warn!("Not having annotation object, cannot delete!");
                debug!("Not deleting annotation object");
            }
        } else {
            debug!("Not deleting annotation object");
        }
    })

            Ok(())
        })
        .collect()
}

fn list(rt: &Runtime) {
    let scmd = rt.cli().subcommand_matches("list").unwrap(); // safed by clap
fn list(rt: &Runtime) -> Result<()> {
    let scmd      = rt.cli().subcommand_matches("list").unwrap(); // safed by clap
    let with_text = scmd.is_present("list-with-text");
    let ids = rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .into_iter();
        .context("No ids supplied")?
        .ok_or_else(|| err_msg("No ids supplied"))?;

    if ids.len() != 0 {
        ids
            .for_each(|id| {
                rt
                    .store()
                    .get(id.clone())
                    .map_err_trace_exit_unwrap()
                    .ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap()
                    .annotations()
                    .map_err_trace_exit_unwrap()
        ids.into_iter()
            .map(|id| -> Result<_> {
                let lds = id.local_display_string();
                Ok(rt.store()
                    .get(id)?
                    .ok_or_else(|| EM::EntryNotFound(lds))?
                    .annotations()?
                    .into_get_iter(rt.store())
                    .trace_unwrap_exit()
                    .map(|opt| opt.ok_or_else(|| format_err!("Cannot find entry")))
                    .trace_unwrap_exit()
                    .map(|el| el.and_then(|o| o.ok_or_else(|| format_err!("Cannot find entry"))))
                    .enumerate()
                    .for_each(|(i, entry)| list_annotation(&rt, i, entry, with_text));
            });
                    .map(|(i, entry)| entry.and_then(|e| list_annotation(&rt, i, e, with_text)))
                    .collect())
            })
            .flatten()
            .collect()
    } else { // ids.len() == 0
        // show them all
        rt.store()
            .all_annotations()
            .map_err_trace_exit_unwrap()
            .all_annotations()?
            .into_get_iter()
            .trace_unwrap_exit()
            .map(|opt| opt.ok_or_else(|| format_err!("Cannot find entry")))
            .trace_unwrap_exit()
            .map(|el| el.and_then(|opt| opt.ok_or_else(|| format_err!("Cannot find entry"))))
            .enumerate()
            .for_each(|(i, entry)| list_annotation(&rt, i, entry, with_text));
            .map(|(i, entry)| entry.and_then(|e| list_annotation(&rt, i, e, with_text)))
            .collect()
    }
}

fn list_annotation<'a>(rt: &Runtime, i: usize, a: FileLockEntry<'a>, with_text: bool) {
fn list_annotation<'a>(rt: &Runtime, i: usize, a: FileLockEntry<'a>, with_text: bool) -> Result<()> {
    if with_text {
        writeln!(rt.stdout(),
                 "--- {i: >5} | {id}\n{text}\n\n",


@@ 261,8 226,6 @@ fn list_annotation<'a>(rt: &Runtime, i: usize, a: FileLockEntry<'a>, with_text: 
                 text = a.get_content())
    } else {
        writeln!(rt.stdout(), "{: >5} | {}", i, a.get_location())
    }
    .to_exit_code()
    .unwrap_or_exit();
    }.map_err(Error::from)
}


M bin/core/imag-category/Cargo.toml => bin/core/imag-category/Cargo.toml +1 -0
@@ 24,6 24,7 @@ log = "0.4.6"
toml = "0.5.1"
toml-query = "0.9.2"
failure = "0.1.5"
resiter = "0.3.0"

libimagstore           = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt              = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-category/src/lib.rs => bin/core/imag-category/src/lib.rs +54 -96
@@ 39,6 39,7 @@ extern crate clap;
extern crate log;
#[macro_use]
extern crate failure;
extern crate resiter;

extern crate libimagentrycategory;
extern crate libimagerror;


@@ 50,19 51,20 @@ use failure::Fallible as Result;
use clap::App;

use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagerror::iter::IterInnerOkOrElse;

mod ui;

use std::io::Write;

use failure::err_msg;
use failure::Error;
use resiter::AndThen;

use libimagentrycategory::store::CategoryStore;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagerror::iter::TraceIterator;
use libimagentrycategory::entry::EntryCategory;
use libimagentrycategory::category::Category;



@@ 73,25 75,22 @@ use libimagentrycategory::category::Category;
pub enum ImagCategory {}
impl ImagApplication for ImagCategory {
    fn run(rt: Runtime) -> Result<()> {
        if let Some(name) = rt.cli().subcommand_name() {
            match name {
                "set"               => set(&rt),
                "get"               => get(&rt),
                "list-category"     => list_category(&rt),
                "create-category"   => create_category(&rt),
                "delete-category"   => delete_category(&rt),
                "list-categories"   => list_categories(&rt),
                other               => {
                    debug!("Unknown command");
                    let _ = rt.handle_unknown_subcommand("imag-category", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .map(::std::process::exit);
                },
            }
        match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? {
            "set"               => set(&rt),
            "get"               => get(&rt),
            "list-category"     => list_category(&rt),
            "create-category"   => create_category(&rt),
            "delete-category"   => delete_category(&rt),
            "list-categories"   => list_categories(&rt),
            other               => {
                debug!("Unknown command");
                if rt.handle_unknown_subcommand("imag-category", other, rt.cli())?.success() {
                    Ok(())
                } else {
                    Err(err_msg("Failed to handle unknown subcommand"))
                }
            },
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 112,125 111,84 @@ impl ImagApplication for ImagCategory {
}


fn set(rt: &Runtime) {
fn set(rt: &Runtime) -> Result<()> {
    let scmd = rt.cli().subcommand_matches("set").unwrap(); // safed by main()
    let name = scmd.value_of("set-name").map(String::from).unwrap(); // safed by clap
    let sids = rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .into_iter();

    StoreIdIterator::new(Box::new(sids.map(Ok)))
    rt.ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .map(Ok)
        .into_get_iter(rt.store())
        .trace_unwrap_exit()
        .map(|o| o.unwrap_or_else(|| {
            error!("Did not find one entry");
            ::std::process::exit(1)
        }))
        .for_each(|mut entry| {
            entry
                .set_category_checked(rt.store(), &name)
                .map_err_trace_exit_unwrap();
        })
        .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
        .and_then_ok(|mut e| e.set_category_checked(rt.store(), &name))
        .collect()
}

fn get(rt: &Runtime) {
fn get(rt: &Runtime) -> Result<()> {
    let out = rt.stdout();
    let mut outlock = out.lock();
    let sids = rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .into_iter();

    StoreIdIterator::new(Box::new(sids.map(Ok)))
    rt.ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .map(Ok)
        .into_get_iter(rt.store())
        .trace_unwrap_exit()
        .map(|o| o.unwrap_or_else(|| {
            error!("Did not find one entry");
            ::std::process::exit(1)
        }))
        .map(|entry| entry.get_category().map_err_trace_exit_unwrap())
        .for_each(|name| {
            writeln!(outlock, "{}", name).to_exit_code().unwrap_or_exit();
        })
        .map(|el| el.and_then(|o| o.ok_or_else(|| err_msg("Did not find one entry"))))
        .map(|entry| entry.and_then(|e| e.get_category()))
        .map(|name| name.and_then(|n| writeln!(outlock, "{}", n).map_err(Error::from)))
        .collect()
}

fn list_category(rt: &Runtime) {
fn list_category(rt: &Runtime) -> Result<()> {
    let scmd = rt.cli().subcommand_matches("list-category").unwrap(); // safed by main()
    let name = scmd.value_of("list-category-name").map(String::from).unwrap(); // safed by clap

    if let Some(category) = rt.store().get_category_by_name(&name).map_err_trace_exit_unwrap() {
    if let Some(category) = rt.store().get_category_by_name(&name)? {
        let out         = rt.stdout();
        let mut outlock = out.lock();

        category
            .get_entries(rt.store())
            .map_err_trace_exit_unwrap()
            .for_each(|entry| {
                writeln!(outlock, "{}", entry.map_err_trace_exit_unwrap().get_location())
                    .to_exit_code()
                    .unwrap_or_exit();
            })
            .map(|entry| writeln!(outlock, "{}", entry?.get_location()).map_err(Error::from))
            .collect()
    } else {
        info!("No category named '{}'", name);
        ::std::process::exit(1)
        Err(format_err!("No category named '{}'", name))
    }
}

fn create_category(rt: &Runtime) {
fn create_category(rt: &Runtime) -> Result<()> {
    let scmd = rt.cli().subcommand_matches("create-category").unwrap(); // safed by main()
    let name = scmd.value_of("create-category-name").map(String::from).unwrap(); // safed by clap

    let _ = rt
        .store()
        .create_category(&name)
        .map_err_trace_exit_unwrap();
    rt.store().create_category(&name).map(|_| ())
}

fn delete_category(rt: &Runtime) {
fn delete_category(rt: &Runtime) -> Result<()> {
    use libimaginteraction::ask::ask_bool;

    let scmd   = rt.cli().subcommand_matches("delete-category").unwrap(); // safed by main()
    let name   = scmd.value_of("delete-category-name").map(String::from).unwrap(); // safed by clap
    let ques   = format!("Do you really want to delete category '{}' and remove links to all categorized enties?", name);

    let mut input = rt.stdin().unwrap_or_else(|| {
        error!("No input stream. Cannot ask for permission");
        ::std::process::exit(1)
    });
    let mut input  = rt.stdin().ok_or_else(|| err_msg("No input stream. Cannot ask for permission"))?;
    let mut output = rt.stdout();
    let answer = ask_bool(&ques, Some(false), &mut input, &mut output).map_err_trace_exit_unwrap();
    let answer = ask_bool(&ques, Some(false), &mut input, &mut output)?;

    if answer {
        info!("Deleting category '{}'", name);
        rt
            .store()
            .delete_category(&name)
            .map_err_trace_exit_unwrap();
        rt.store().delete_category(&name).map(|_| ())
    } else {
        info!("Not doing anything");
        Ok(())
    }
}

fn list_categories(rt: &Runtime) {
fn list_categories(rt: &Runtime) -> Result<()> {
    let out         = rt.stdout();
    let mut outlock = out.lock();

    rt.store()
        .all_category_names()
        .map_err_trace_exit_unwrap()
        .for_each(|name| {
            writeln!(outlock, "{}", name.map_err_trace_exit_unwrap())
                .to_exit_code()
                .unwrap_or_exit();
        })
        .all_category_names()?
        .map(|name| name.and_then(|n| writeln!(outlock, "{}", n).map_err(Error::from)))
        .collect()
}


M bin/core/imag-create/src/lib.rs => bin/core/imag-create/src/lib.rs +5 -20
@@ 36,21 36,19 @@

extern crate clap;
extern crate failure;
#[macro_use] extern crate log;

extern crate libimagerror;
extern crate libimagrt;
extern crate libimagstore;

use failure::Fallible as Result;
use failure::err_msg;
use clap::App;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagerror::trace::MapErrTrace;
use libimagstore::iter::create::StoreIdCreateIteratorExtension;
use libimagstore::iter::retrieve::StoreIdRetrieveIteratorExtension;
use libimagerror::exit::ExitUnwrap;

mod ui;



@@ 60,30 58,17 @@ pub enum ImagCreate {}
impl ImagApplication for ImagCreate {
    fn run(rt: Runtime) -> Result<()> {
        let force = rt.cli().is_present("force");
        debug!("Detected force = {}", force);

        let ids = rt.ids::<crate::ui::PathProvider>()
            .map_err_trace_exit_unwrap()
            .unwrap_or_else(|| {
                error!("No ids supplied");
                ::std::process::exit(1);
            })

        let ids = rt.ids::<crate::ui::PathProvider>()?
            .ok_or_else(|| err_msg("No ids supplied"))?
            .into_iter()
            .map(|id| { debug!("id = {}", id); id })
            .map(Ok);

        if force {
            ids.into_retrieve_iter(rt.store()).collect::<Result<Vec<_>>>()
        } else {
            ids.into_create_iter(rt.store()).collect::<Result<Vec<_>>>()
        }.map_err_trace_exit_unwrap()
        .into_iter()
        .for_each(|el| {
            rt.report_touched(el.get_location()).unwrap_or_exit();
            trace!("Entry = {}", el.get_location());
        });

        Ok(())
        }.map(|_| ())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {

M bin/core/imag-diagnostics/Cargo.toml => bin/core/imag-diagnostics/Cargo.toml +1 -0
@@ 19,6 19,7 @@ toml = "0.5.1"
toml-query = "0.9.2"
indicatif  = "0.12.0"
failure    = "0.1.5"
resiter    = "0.3.0"

libimagstore     = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-diagnostics/src/lib.rs => bin/core/imag-diagnostics/src/lib.rs +32 -59
@@ 39,6 39,7 @@ extern crate toml;
extern crate toml_query;
extern crate indicatif;
extern crate failure;
extern crate resiter;
#[macro_use] extern crate log;

extern crate libimagrt;


@@ 50,20 51,18 @@ use std::io::Write;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagerror::trace::MapErrTrace;
use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagentrylink::linkable::Linkable;
use libimagerror::iter::IterInnerOkOrElse;

use toml::Value;
use toml_query::read::TomlValueReadExt;
use indicatif::{ProgressBar, ProgressStyle};
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use clap::App;
use resiter::AndThen;

use std::collections::BTreeMap;



@@ 106,20 105,6 @@ impl Diagnostic {
    }
}

macro_rules! do_write {
    ($dest:ident, $pattern:tt) => {
        let _ = writeln!($dest, $pattern)
            .to_exit_code()
            .unwrap_or_exit();
    };

    ($dest:ident, $pattern:tt, $( $args:expr ),*) => {
        let _ = writeln!($dest, $pattern, $( $args ),*)
            .to_exit_code()
            .unwrap_or_exit();
    }
}

/// Marker enum for implementing ImagApplication on
///
/// This is used by binaries crates to execute business logic


@@ 127,8 112,8 @@ macro_rules! do_write {
pub enum ImagDiagnostics {}
impl ImagApplication for ImagDiagnostics {
    fn run(rt: Runtime) -> Result<()> {
        let template    = get_config(&rt, "rt.progressbar_style");
        let tick_chars  = get_config(&rt, "rt.progressticker_chars");
        let template    = get_config(&rt, "rt.progressbar_style")?;
        let tick_chars  = get_config(&rt, "rt.progressticker_chars")?;
        let verbose     = rt.cli().is_present("more-output");

        let style = if let Some(tick_chars) = tick_chars {


@@ 143,22 128,16 @@ impl ImagApplication for ImagDiagnostics {
        spinner.set_message("Accumulating data");

        let diags = rt.store()
            .entries()
            .map_err_trace_exit_unwrap()
            .entries()?
            .into_get_iter()
            .map(|e| {
                e.map_err_trace_exit_unwrap()
                    .ok_or_else(|| Error::from(err_msg("Unable to get entry".to_owned())))
                    .map_err_trace_exit_unwrap()
            })
            .map(|e| {
            .map_inner_ok_or_else(|| err_msg("Unable to get entry"))
            .and_then_ok(|e| {
                let diag = Diagnostic::for_entry(&e);
                debug!("Diagnostic for '{:?}' = {:?}", e.get_location(), diag);
                drop(e);
                diag
            })
            .collect::<Result<Vec<_>>>()
            .map_err_trace_exit_unwrap();
            .collect::<Result<Vec<_>>>()?;

        spinner.finish();
        let n                = diags.len();


@@ 220,38 199,37 @@ impl ImagApplication for ImagDiagnostics {

        let mut out = rt.stdout();

        do_write!(out, "imag version {}", { env!("CARGO_PKG_VERSION") });
        do_write!(out, "");
        do_write!(out, "{} entries", n);
        write!(out, "imag version {}", { env!("CARGO_PKG_VERSION") })?;
        write!(out, "")?;
        write!(out, "{} entries", n)?;

        for (k, v) in version_counts {
            do_write!(out, "{} entries with store version '{}'", v, k);
            write!(out, "{} entries with store version '{}'", v, k)?;
        }
        if n != 0 {
            do_write!(out, "{} header sections in the average entry", sum_header_sections / n);
            do_write!(out, "{} average content bytecount", sum_bytecount_content / n);
            do_write!(out, "{} average overall bytecount", sum_overall_byte_size / n);
            write!(out, "{} header sections in the average entry", sum_header_sections / n)?;
            write!(out, "{} average content bytecount", sum_bytecount_content / n)?;
            write!(out, "{} average overall bytecount", sum_overall_byte_size / n)?;

            if let Some((num, path)) = max_overall_byte_size {
                do_write!(out, "Largest Entry ({} bytes): {}", num, path.local_display_string());
                write!(out, "Largest Entry ({} bytes): {}", num, path.local_display_string())?;
            }

            do_write!(out, "{} average internal link count per entry", num_links / n);
            write!(out, "{} average internal link count per entry", num_links / n)?;

            if let Some((num, path)) = max_links {
                do_write!(out, "Entry with most internal links ({}): {}",
                          num,
                          path.local_display_string());
                write!(out, "Entry with most internal links ({}): {}",
                         num,
                         path.local_display_string())?;
            }
            do_write!(out, "{} verified entries", verified_count);
            do_write!(out, "{} unverified entries", unverified_count);
            write!(out, "{} verified entries", verified_count)?;
            write!(out, "{} unverified entries", unverified_count)?;
            if verbose {
                for unve in unverified_entries.iter() {
                    do_write!(out, "Unverified: {}", unve);
                    write!(out, "Unverified: {}", unve)?;
                }
            }
        }

        Ok(())
    }



@@ 272,17 250,12 @@ impl ImagApplication for ImagDiagnostics {
    }
}

fn get_config(rt: &Runtime, s: &'static str) -> Option<String> {
    rt.config().and_then(|cfg| {
        cfg.read(s)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
            .map(|opt| match opt {
                &Value::String(ref s) => s.to_owned(),
                _ => {
                    error!("Config type wrong: 'rt.progressbar_style' should be a string");
                    ::std::process::exit(1)
                }
            })
    })
fn get_config(rt: &Runtime, s: &'static str) -> Result<Option<String>> {
    let cfg = rt.config().ok_or_else(|| err_msg("No configuration"))?;

    match cfg.read(s)? {
        Some(&Value::String(ref s)) => Ok(Some(s.to_owned())),
        Some(_) => Err(err_msg("Config type wrong: 'rt.progressbar_style' should be a string")),
        None => Ok(None),
    }
}

M bin/core/imag-edit/Cargo.toml => bin/core/imag-edit/Cargo.toml +1 -0
@@ 25,6 25,7 @@ version      = "3.0.0"
toml         = "0.5.1"
toml-query   = "0.9.2"
failure      = "0.1.5"
resiter      = "0.3.0"

libimagstore     = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-edit/src/lib.rs => bin/core/imag-edit/src/lib.rs +16 -31
@@ 37,6 37,7 @@
extern crate clap;
#[macro_use] extern crate log;
extern crate failure;
extern crate resiter;

extern crate libimagentryedit;
extern crate libimagerror;


@@ 44,16 45,16 @@ extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagentryedit::edit::Edit;
use libimagentryedit::edit::EditHeader;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagerror::iter::IterInnerOkOrElse;

use failure::Fallible as Result;
use failure::err_msg;
use resiter::AndThen;
use clap::App;

mod ui;


@@ 68,39 69,23 @@ impl ImagApplication for ImagEdit {
        let edit_header = rt.cli().is_present("edit-header");
        let edit_header_only = rt.cli().is_present("edit-header-only");

        let sids = rt
            .ids::<crate::ui::PathProvider>()
            .map_err_trace_exit_unwrap()
            .unwrap_or_else(|| {
                error!("No ids supplied");
                ::std::process::exit(1);
            })
            .into_iter();

        StoreIdIterator::new(Box::new(sids.into_iter().map(Ok)))
        rt.ids::<crate::ui::PathProvider>()?
            .ok_or_else(|| err_msg("No ids supplied"))?
            .into_iter()
            .map(Ok)
            .into_get_iter(rt.store())
            .trace_unwrap_exit()
            .map(|o| o.unwrap_or_else(|| {
                error!("Did not find one entry");
                ::std::process::exit(1)
            }))
            .for_each(|mut entry| {
            .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
            .inspect(|e| debug!("Editing = {:?}", e))
            .and_then_ok(|mut entry| {
                if edit_header {
                    let _ = entry
                        .edit_header_and_content(&rt)
                        .map_err_trace_exit_unwrap();
                    entry.edit_header_and_content(&rt)
                } else if edit_header_only {
                    let _ = entry
                        .edit_header(&rt)
                        .map_err_trace_exit_unwrap();
                    entry.edit_header(&rt)
                } else {
                    let _ = entry
                        .edit_content(&rt)
                        .map_err_trace_exit_unwrap();
                    entry.edit_content(&rt)
                }
            });

        Ok(())
            })
            .collect()
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {

M bin/core/imag-git/src/lib.rs => bin/core/imag-git/src/lib.rs +31 -70
@@ 38,12 38,11 @@ extern crate clap;
#[macro_use] extern crate log;
extern crate toml;
extern crate toml_query;
extern crate failure;
#[macro_use] extern crate failure;

extern crate libimagrt;
extern crate libimagerror;

use std::io::Write;
use std::io::ErrorKind;
use std::process::Command;



@@ 51,9 50,9 @@ use toml::Value;
use toml_query::read::TomlValueReadExt;
use clap::App;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;

use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;



@@ 68,29 67,19 @@ impl ImagApplication for ImagGit {
    fn run(rt: Runtime) -> Result<()> {
        let execute_in_store = rt
            .config()
            .unwrap_or_else(|| {
                error!("No configuration. Please use git yourself, not via imag-git");
                error!("Won't continue without configuration.");
                ::std::process::exit(1);
            })
            .ok_or_else(|| err_msg("No configuration. Please use git yourself, not via imag-git"))
            .context("Won't continue without configuration.")
            ?
            .read("git.execute_in_store")
            .unwrap_or_else(|e| {
                error!("Failed to read config setting 'git.execute_in_store'");
                error!("-> {:?}", e);
                ::std::process::exit(1)
            })
            .unwrap_or_else(|| {
                error!("Missing config setting 'git.execute_in_store'");
                ::std::process::exit(1)
            });
            .context("Failed to read config setting 'git.execute_in_store'")
            ?
            .ok_or_else(|| err_msg("Missing config setting 'git.execute_in_store'"))
            ?;

        let execute_in_store = match *execute_in_store {
            Value::Boolean(b) => b,
            _ => {
                error!("Type error: 'git.execute_in_store' is not a boolean!");
                ::std::process::exit(1)
            }
        };
            Value::Boolean(b) => Ok(b),
            _ => Err(err_msg("Type error: 'git.execute_in_store' is not a boolean!")),
        }?;

        let execpath = if execute_in_store {
            rt.store().path().to_str()


@@ 98,11 87,7 @@ impl ImagApplication for ImagGit {
            rt.rtp().to_str()
        }
        .map(String::from)
            .unwrap_or_else(|| {
                error!("Cannot parse to string: {:?}", rt.store().path());
                ::std::process::exit(1)
            });

        .ok_or_else(|| format_err!("Cannot parse to string: {:?}", rt.store().path()))?;

        let mut command = Command::new("git");
        command


@@ 120,63 105,39 @@ impl ImagApplication for ImagGit {
        debug!("Adding args = {:?}", args);
        command.args(&args);

        match rt.cli().subcommand() {
            (external, Some(ext_m)) => {
                command.arg(external);
                let args = ext_m
                    .values_of("")
                    .map(|vs| vs.map(String::from).collect())
                    .unwrap_or_else(|| vec![]);
        if let (external, Some(ext_m)) = rt.cli().subcommand() {
            command.arg(external);
            let args = ext_m
                .values_of("")
                .map(|vs| vs.map(String::from).collect())
                .unwrap_or_else(|| vec![]);

                debug!("Adding subcommand '{}' and args = {:?}", external, args);
                command.args(&args);
            },
            _ => {},
            debug!("Adding subcommand '{}' and args = {:?}", external, args);
            command.args(&args);
        }

        let mut out = rt.stdout();

        debug!("Calling: {:?}", command);

        match command.spawn().and_then(|mut c| c.wait()) {
            Ok(exit_status) => {
                if !exit_status.success() {
                    debug!("git exited with non-zero exit code: {:?}", exit_status);
                    let mut err = rt.stderr();
                    writeln!(err, "git exited with non-zero exit code")
                        .to_exit_code()
                        .unwrap_or_exit();
                    ::std::process::exit(exit_status.code().unwrap_or(1));
                    Err(format_err!("git exited with non-zero exit code: {:?}", exit_status))
                } else {
                    debug!("Successful exit!");
                    Ok(())
                }
                debug!("Successful exit!");
            },

            Err(e) => {
                debug!("Error calling git");
                match e.kind() {
                    ErrorKind::NotFound => {
                        let _ = writeln!(out, "Cannot find 'git' executable")
                            .to_exit_code()
                            .unwrap_or_exit();
                        ::std::process::exit(1);
                    },
                    ErrorKind::PermissionDenied => {
                        let _ = writeln!(out, "No permission to execute: 'git'")
                            .to_exit_code()
                            .unwrap_or_exit();
                        ::std::process::exit(1);
                    },
                    _ => {
                        let _ = writeln!(out, "Error spawning: {:?}", e)
                            .to_exit_code()
                            .unwrap_or_exit();
                        ::std::process::exit(1);
                    }
                }
                Err(match e.kind() {
                    ErrorKind::NotFound         => err_msg("Cannot find 'git' executable"),
                    ErrorKind::PermissionDenied => err_msg("No permission to execute: 'git'"),
                    _                           => format_err!("Error spawning: {:?}", e),
                })
            }
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {

M bin/core/imag-gps/src/lib.rs => bin/core/imag-gps/src/lib.rs +65 -98
@@ 45,12 45,11 @@ extern crate libimagerror;
extern crate libimagstore;

use std::io::Write;
use std::process::exit;
use std::str::FromStr;


use failure::err_msg;
use failure::Error;
use failure::Fallible as Result;
use failure::err_msg;
use clap::App;

use libimagstore::storeid::StoreId;


@@ 58,9 57,6 @@ use libimagentrygps::types::*;
use libimagentrygps::entry::*;
use libimagrt::application::ImagApplication;
use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;

mod ui;



@@ 71,22 67,22 @@ mod ui;
pub enum ImagGps {}
impl ImagApplication for ImagGps {
    fn run(rt: Runtime) -> Result<()> {
        if let Some(name) = rt.cli().subcommand_name() {
            match name {
                "add"    => add(&rt),
                "remove" => remove(&rt),
                "get"    => get(&rt),
                other    => {
                    debug!("Unknown command");
                    let _ = rt.handle_unknown_subcommand("imag-gps", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .map(::std::process::exit);
        match rt.cli().subcommand_name().ok_or_else(|| err_msg("No subcommand called"))? {
            "add"    => add(&rt),
            "remove" => remove(&rt),
            "get"    => get(&rt),
            other    => {
                debug!("Unknown command");
                if rt.handle_unknown_subcommand("imag-gps", other, rt.cli())
                    .map_err(Error::from)?
                    .success()
                {
                    Ok(())
                } else {
                    Err(format_err!("Subcommand failed"))
                }
            }
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 106,46 102,33 @@ impl ImagApplication for ImagGps {
    }
}

fn rt_get_ids(rt: &Runtime) -> Vec<StoreId> {
fn rt_get_ids(rt: &Runtime) -> Result<Vec<StoreId>> {
    rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))
}

fn add(rt: &Runtime) {
fn add(rt: &Runtime) -> Result<()> {
    let c = {
        let parse = |value: &str| -> (i64, i64, i64) {
        let parse = |value: &str| -> Result<(i64, i64, i64)> {
            debug!("Parsing '{}' into degree, minute and second", value);
            let ary = value.split('.')
                .map(|v| {debug!("Parsing = {}", v); v})
                .map(FromStr::from_str)
                .map(|elem| {
                    elem.or_else(|_| Err(err_msg("Error while converting number")))
                        .map_err_trace_exit_unwrap()
                })
                .collect::<Vec<i64>>();

            let degree = ary.get(0).unwrap_or_else(|| {
                error!("Degree missing. This value is required.");
                exit(1)
            });
            let minute = ary.get(1).unwrap_or_else(|| {
                error!("Degree missing. This value is required.");
                exit(1)
            });
                .map(|elem| elem.or_else(|_| Err(err_msg("Error while converting number"))))
                .collect::<Result<Vec<i64>>>()?;

            let degree = ary.get(0).ok_or_else(|| err_msg("Degree missing. This value is required."))?;
            let minute = ary.get(1).ok_or_else(|| err_msg("Degree missing. This value is required."))?;
            let second = ary.get(2).unwrap_or(&0);

            (*degree, *minute, *second)
            Ok((*degree, *minute, *second))
        };

        let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main()

        let long = parse(scmd.value_of("longitude").unwrap()); // unwrap safed by clap
        let lati = parse(scmd.value_of("latitude").unwrap()); // unwrap safed by clap
        let long = parse(scmd.value_of("longitude").unwrap())?; // unwrap safed by clap
        let lati = parse(scmd.value_of("latitude").unwrap())?; // unwrap safed by clap

        let long = GPSValue::new(long.0, long.1, long.2);
        let lati = GPSValue::new(lati.0, lati.1, lati.2);


@@ 153,82 136,66 @@ fn add(rt: &Runtime) {
        Coordinates::new(long, lati)
    };

    rt_get_ids(&rt)
    rt_get_ids(&rt)?
        .into_iter()
        .for_each(|id| {
        .map(|id| {
            rt.store()
                .get(id.clone())
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| { // if we have Ok(None)
                    error!("No such entry: {}", id);
                    exit(1)
                })
                .set_coordinates(c.clone())
                .map_err_trace_exit_unwrap();

            rt.report_touched(&id).unwrap_or_exit();
        });
                .get(id.clone())?
                .ok_or_else(|| format_err!("No such entry: {}", id))?
                .set_coordinates(c.clone())?;

            rt.report_touched(&id).map_err(Error::from)
        })
        .collect()
}

fn remove(rt: &Runtime) {
fn remove(rt: &Runtime) -> Result<()> {
    let print_removed = rt
        .cli()
        .subcommand_matches("remove")
        .unwrap()
        .is_present("print-removed"); // safed by main()

    rt_get_ids(&rt)
    rt_get_ids(&rt)?
        .into_iter()
        .for_each(|id| {
            let removed_value = rt
        .map(|id| {
            let removed_value : Coordinates = rt
                .store()
                .get(id.clone())
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| { // if we have Ok(None)
                    error!("No such entry: {}", id);
                    exit(1)
                })
                .remove_coordinates()
                .map_err_trace_exit_unwrap() // The delete action failed
                .unwrap_or_else(|| { // if we have Ok(None)
                    error!("Entry had no coordinates: {}", id);
                    exit(1)
                })
                .map_err_trace_exit_unwrap(); // The parsing of the deleted values failed
                .get(id.clone())?
                .ok_or_else(|| format_err!("No such entry: {}", id))?
                .remove_coordinates()?
                .ok_or_else(|| format_err!("Entry had no coordinates: {}", id))??;

            if print_removed {
                writeln!(rt.stdout(), "{}", removed_value).to_exit_code().unwrap_or_exit();
                writeln!(rt.stdout(), "{}", removed_value)?;
            }

            rt.report_touched(&id).unwrap_or_exit();
        });
            rt.report_touched(&id).map_err(Error::from)
        })
        .collect()
}

fn get(rt: &Runtime) {
fn get(rt: &Runtime) -> Result<()> {
    let mut stdout = rt.stdout();

    rt_get_ids(&rt)
    rt_get_ids(&rt)?
        .into_iter()
        .for_each(|id| {
        .map(|id| {
            let value = rt
                .store()
                .get(id.clone())
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| { // if we have Ok(None)
                    error!("No such entry: {}", id);
                    exit(1)
                })
                .get_coordinates()
                .map_err_trace_exit_unwrap() // The get action failed
                .unwrap_or_else(|| { // if we have Ok(None)
                    error!("Entry has no coordinates: {}", id);
                    exit(1)
                });

            writeln!(stdout, "{}", value).to_exit_code().unwrap_or_exit();

            rt.report_touched(&id).unwrap_or_exit();
                .get(id.clone())?
                .ok_or_else(|| { // if we have Ok(None)
                    format_err!("No such entry: {}", id)
                })?
                .get_coordinates()?
                .ok_or_else(|| { // if we have Ok(None)
                    format_err!("Entry has no coordinates: {}", id)
                })?;

            writeln!(stdout, "{}", value)?;

            rt.report_touched(&id).map_err(Error::from)
        })

        .collect()
}


M bin/core/imag-grep/Cargo.toml => bin/core/imag-grep/Cargo.toml +1 -0
@@ 23,6 23,7 @@ maintenance                       = { status     = "actively-developed" }
log  = "0.4.6"
regex = "1.1.7"
failure = "0.1.5"
resiter = "0.3.0"

libimagstore     = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-grep/src/lib.rs => bin/core/imag-grep/src/lib.rs +30 -28
@@ 35,9 35,10 @@
)]

#[macro_use] extern crate log;
#[macro_use] extern crate failure;
extern crate clap;
extern crate regex;
extern crate failure;
extern crate resiter;

extern crate libimagstore;
extern crate libimagrt;


@@ 46,15 47,17 @@ extern crate libimagerror;
use std::io::Write;

use regex::Regex;
use failure::Fallible as Result;
use clap::App;
use failure::Error;
use failure::Fallible as Result;
use failure::err_msg;
use resiter::AndThen;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagstore::store::Entry;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagerror::iter::IterInnerOkOrElse;


mod ui;



@@ 82,34 85,32 @@ impl ImagApplication for ImagGrep {
            .value_of("pattern")
            .map(Regex::new)
            .unwrap() // ensured by clap
            .unwrap_or_else(|e| {
                error!("Regex building error: {:?}", e);
                ::std::process::exit(1)
            });
            .map_err(|e| format_err!("Regex building error: {:?}", e))?;

        let overall_count = rt
            .store()
            .entries()
            .map_err_trace_exit_unwrap()
            .entries()?
            .into_get_iter()
            .filter_map(|res| res.map_err_trace_exit_unwrap())
            .filter_map(|entry| if pattern.is_match(entry.get_content()) {
                show(&rt, &entry, &pattern, &opts, &mut count);
                Some(())
            } else {
                None
            .map_inner_ok_or_else(|| err_msg("Entry from entries missing"))
            .and_then_ok(|entry| {
                if pattern.is_match(entry.get_content()) {
                    debug!("Matched: {}", entry.get_location());
                    show(&rt, &entry, &pattern, &opts, &mut count)
                } else {
                    debug!("Not matched: {}", entry.get_location());
                    Ok(())
                }
            })
            .count();
            .collect::<Result<Vec<_>>>()?
            .len();

        if opts.count {
            writeln!(rt.stdout(), "{}", count).to_exit_code().unwrap_or_exit();
            writeln!(rt.stdout(), "{}", count)?;
        } else if !opts.files_with_matches {
            writeln!(rt.stdout(), "Processed {} files, {} matches, {} nonmatches",
                     overall_count,
                     count,
                     overall_count - count)
                .to_exit_code()
                .unwrap_or_exit();
                     overall_count - count)?;
        }

        Ok(())


@@ 130,27 131,28 @@ impl ImagApplication for ImagGrep {
    fn version() -> &'static str {
        env!("CARGO_PKG_VERSION")
    }

}

fn show(rt: &Runtime, e: &Entry, re: &Regex, opts: &Options, count: &mut usize) {
fn show(rt: &Runtime, e: &Entry, re: &Regex, opts: &Options, count: &mut usize) -> Result<()> {
    if opts.files_with_matches {
        writeln!(rt.stdout(), "{}", e.get_location()).to_exit_code().unwrap_or_exit();
        writeln!(rt.stdout(), "{}", e.get_location())?;
    } else if opts.count {
        *count += 1;
    } else {
        writeln!(rt.stdout(), "{}:", e.get_location()).to_exit_code().unwrap_or_exit();
        writeln!(rt.stdout(), "{}:", e.get_location())?;
        for capture in re.captures_iter(e.get_content()) {
            for mtch in capture.iter() {
                if let Some(m) = mtch {
                    writeln!(rt.stdout(), " '{}'", m.as_str()).to_exit_code().unwrap_or_exit();
                    writeln!(rt.stdout(), " '{}'", m.as_str())?;
                }
            }
        }

        writeln!(rt.stdout()).to_exit_code().unwrap_or_exit();
        writeln!(rt.stdout())?;
        *count += 1;
    }

    rt.report_touched(e.get_location()).unwrap_or_exit();
    rt.report_touched(e.get_location()).map_err(Error::from)
}


M bin/core/imag-header/Cargo.toml => bin/core/imag-header/Cargo.toml +1 -0
@@ 26,6 26,7 @@ toml         = "0.5.1"
toml-query   = "0.9.2"
filters      = "0.3.0"
failure      = "0.1.5"
resiter      = "0.3.0"

libimagstore     = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-header/src/lib.rs => bin/core/imag-header/src/lib.rs +100 -116
@@ 34,10 34,11 @@

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate failure;
extern crate toml;
extern crate toml_query;
extern crate filters;
extern crate failure;
extern crate resiter;

extern crate libimagentryedit;
extern crate libimagerror;


@@ 52,18 53,17 @@ use std::string::ToString;
use clap::{App, ArgMatches};
use filters::filter::Filter;
use toml::Value;
use failure::{Fallible as Result, Error};
use failure::Error;
use failure::Fallible as Result;
use failure::err_msg;
use resiter::FilterMap;
use resiter::AndThen;

use libimagerror::exit::ExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagerror::iter::TraceIterator;
use libimagerror::trace::MapErrTrace;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreIdIterator;
use libimagerror::iter::IterInnerOkOrElse;

use toml_query::read::TomlValueReadExt;
use toml_query::read::TomlValueReadTypeExt;


@@ 85,22 85,16 @@ impl ImagApplication for ImagHeader {
        trace!("list_output_with_ids     = {:?}", list_output_with_ids );
        trace!("list_output_with_ids_fmt = {:?}", list_output_with_ids_fmt);

        let sids = rt
            .ids::<crate::ui::PathProvider>()
            .map_err_trace_exit_unwrap()
            .unwrap_or_else(|| {
                error!("No ids supplied");
                ::std::process::exit(1);
            })
            .into_iter();

        let iter = StoreIdIterator::new(Box::new(sids.map(Ok)))
        let iter = rt
            .ids::<crate::ui::PathProvider>()?
            .ok_or_else(|| err_msg("No ids supplied"))?
            .into_iter()
            .map(Ok)
            .into_get_iter(rt.store())
            .trace_unwrap_exit()
            .filter_map(|x| x);
            .map_inner_ok_or_else(|| err_msg("Did not find one entry"));

        match rt.cli().subcommand() {
            ("read", Some(mtch))   => ::std::process::exit(read(&rt, mtch, iter)),
            ("read", Some(mtch))   => read(&rt, mtch, iter),
            ("has", Some(mtch))    => has(&rt, mtch, iter),
            ("hasnt", Some(mtch))  => hasnt(&rt, mtch, iter),
            ("int", Some(mtch))    => int(&rt, mtch, iter),


@@ 109,16 103,16 @@ impl ImagApplication for ImagHeader {
            ("bool", Some(mtch))   => boolean(&rt, mtch, iter),
            (other, _mtchs) => {
                debug!("Unknown command");
                ::std::process::exit({
                    rt.handle_unknown_subcommand("imag-header", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .unwrap_or(1)
                });
                if rt.handle_unknown_subcommand("imag-header", other, rt.cli())
                    .map_err(Error::from)?
                    .success()
                {
                    Ok(())
                } else {
                    Err(format_err!("Subcommand failed"))
                }
            },
        };

        Ok(())
        }
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 138,21 132,20 @@ impl ImagApplication for ImagHeader {
    }
}

fn read<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> i32
    where I: Iterator<Item = FileLockEntry<'e>>
fn read<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: reading value");
    let header_path = get_header_path(mtch, "header-value-path");
    let mut output = rt.stdout();
    trace!("Got output: {:?}", output);

    iter.fold(0, |accu, entry| {
    iter.and_then_ok(|entry| {
        trace!("Processing headers: working on {:?}", entry.get_location());
        entry.get_header()
            .read(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
            .map(|value| {
            .read(header_path)?
            .ok_or_else(|| format_err!("Value not present for entry {} at {}", entry.get_location(), header_path))
            .and_then(|value| {
                trace!("Processing headers: Got value {:?}", value);

                let string_representation = match value {


@@ 164,65 157,56 @@ fn read<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> i32
                };

                if let Some(repr) = string_representation {
                    writeln!(output, "{}", repr)
                    writeln!(output, "{}", repr)?;
                } else {
                    writeln!(output, "{}", value)
                    writeln!(output, "{}", value)?;
                }
                .to_exit_code()
                .map(|_| accu)
                .unwrap_or_else(ExitCode::code)
            })
            .unwrap_or_else(|| {
                // if value not present and configured
                error!("Value not present for entry {} at {}", entry.get_location(), header_path);
                1
                Ok(())
            })
    })
    .collect::<Result<()>>()
}

fn has<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
    where I: Iterator<Item = FileLockEntry<'e>>
fn has<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: has value");
    let header_path = get_header_path(mtch, "header-value-path");
    let mut output = rt.stdout();

    iter.for_each(|entry| {
    iter.and_then_ok(|entry| {
        trace!("Processing headers: working on {:?}", entry.get_location());
        if entry.get_header()
            .read(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
            .is_some()
            {
                rt.report_touched(entry.get_location()).unwrap_or_exit();
                if !rt.output_is_pipe() {
                    writeln!(output, "{}", entry.get_location()).to_exit_code().unwrap_or_exit();
                }
        if let Some(_) = entry.get_header().read(header_path)?  {
            if !rt.output_is_pipe() {
                writeln!(output, "{}", entry.get_location())?;
            }
            rt.report_touched(entry.get_location()).map_err(Error::from)
        } else {
            Ok(())
        }
    })
    .collect::<Result<()>>()
}

fn hasnt<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
    where I: Iterator<Item = FileLockEntry<'e>>
fn hasnt<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: hasnt value");
    let header_path = get_header_path(mtch, "header-value-path");
    let mut output = rt.stdout();

    iter.for_each(|entry| {
    iter.and_then_ok(|entry| {
        trace!("Processing headers: working on {:?}", entry.get_location());
        if entry.get_header()
            .read(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
            .is_none() {
                rt.report_touched(entry.get_location()).unwrap_or_exit();
                if !rt.output_is_pipe() {
                    writeln!(output, "{}", entry.get_location()).to_exit_code().unwrap_or_exit();
                }
        if let Some(_) = entry.get_header().read(header_path)? {
            Ok(())
        } else {
            if !rt.output_is_pipe() {
                writeln!(output, "{}", entry.get_location())?;
            }
            rt.report_touched(entry.get_location()).map_err(Error::from)
        }
    })
    .collect()
}

macro_rules! implement_compare {


@@ 238,8 222,8 @@ macro_rules! implement_compare {
    }}
}

fn int<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
    where I: Iterator<Item = FileLockEntry<'e>>
fn int<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: int value");
    let header_path = get_header_path(mtch, "header-value-path");


@@ 264,20 248,20 @@ fn int<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
            implement_compare!(mtch, "header-int-gte", i64, |cmp| *i >= cmp)
        });

    iter.filter(|entry| if let Some(hdr) = entry.get_header()
            .read_int(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
        {
            filter.filter(&hdr)
    iter.and_then_ok(|entry| {
        if let Some(hdr) = entry.get_header().read_int(header_path)?  {
            Ok((filter.filter(&hdr), entry))
        } else {
            false
        })
        .for_each(|entry| rt.report_touched(entry.get_location()).unwrap_or_exit())
            Ok((false, entry))
        }
    })
    .filter_map_ok(|(b, e)| if b { Some(e) } else { None })
    .and_then_ok(|entry| rt.report_touched(entry.get_location()).map_err(Error::from))
    .collect()
}

fn float<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
    where I: Iterator<Item = FileLockEntry<'e>>
fn float<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: float value");
    let header_path = get_header_path(mtch, "header-value-path");


@@ 302,20 286,20 @@ fn float<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
            implement_compare!(mtch, "header-float-gte", f64, |cmp| *i >= cmp)
        });

    iter.filter(|entry| if let Some(hdr) = entry.get_header()
            .read_float(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
        {
            filter.filter(&hdr)
    iter.and_then_ok(|entry| {
        if let Some(hdr) = entry.get_header().read_float(header_path)? {
            Ok((filter.filter(&hdr), entry))
        } else {
            false
        })
        .for_each(|entry| rt.report_touched(entry.get_location()).unwrap_or_exit())
            Ok((false, entry))
        }
    })
    .filter_map_ok(|(b, e)| if b { Some(e) } else { None })
    .and_then_ok(|entry| rt.report_touched(entry.get_location()).map_err(Error::from))
    .collect()
}

fn string<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
    where I: Iterator<Item = FileLockEntry<'e>>
fn string<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: string value");
    let header_path = get_header_path(mtch, "header-value-path");


@@ 328,20 312,20 @@ fn string<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
            implement_compare!(mtch, "header-string-neq", String, |cmp| *i != cmp)
        });

    iter.filter(|entry| if let Some(hdr) = entry.get_header()
            .read_string(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
        {
            filter.filter(&hdr)
    iter.and_then_ok(|entry| {
        if let Some(hdr) = entry.get_header().read_string(header_path)?  {
            Ok((filter.filter(&hdr), entry))
        } else {
            false
        })
        .for_each(|entry| rt.report_touched(entry.get_location()).unwrap_or_exit())
            Ok((false, entry))
        }
    })
    .filter_map_ok(|(b, e)| if b { Some(e) } else { None })
    .and_then_ok(|entry| rt.report_touched(entry.get_location()).map_err(Error::from))
    .collect()
}

fn boolean<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
    where I: Iterator<Item = FileLockEntry<'e>>
fn boolean<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I) -> Result<()>
    where I: Iterator<Item = Result<FileLockEntry<'e>>>
{
    debug!("Processing headers: bool value");
    let header_path = get_header_path(mtch, "header-value-path");


@@ 350,16 334,16 @@ fn boolean<'a, 'e, I>(rt: &Runtime, mtch: &ArgMatches<'a>, iter: I)
        .and(|i: &bool| -> bool { *i })
        .and(|i: &bool| -> bool { *i });

    iter.filter(|entry| if let Some(hdr) = entry.get_header()
            .read_bool(header_path)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap()
        {
            filter.filter(&hdr)
    iter.and_then_ok(|entry| {
        if let Some(hdr) = entry.get_header().read_bool(header_path)?  {
            Ok((filter.filter(&hdr), entry))
        } else {
            false
        })
        .for_each(|entry| rt.report_touched(entry.get_location()).unwrap_or_exit())
            Ok((false, entry))
        }
    })
    .filter_map_ok(|(b, e)| if b { Some(e) } else { None })
    .and_then_ok(|entry| rt.report_touched(entry.get_location()).map_err(Error::from))
    .collect()
}



M bin/core/imag-id-in-collection/src/main.rs => bin/core/imag-id-in-collection/src/main.rs +12 -15
@@ 51,12 51,12 @@ extern crate libimagstore;
use std::io::Write;

use filters::filter::Filter;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;

use libimagstore::storeid::StoreId;
use libimagrt::setup::generate_runtime_setup;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;

mod ui;



@@ 83,7 83,7 @@ impl<'a, A> Filter<StoreId> for IsInCollectionsFilter<'a, A>
}


fn main() {
fn main() -> Result<()> {
    let version = make_imag_version!();
    let rt = generate_runtime_setup("imag-id-in-collection",
                                    &version,


@@ 99,22 99,19 @@ fn main() {
    let mut stdout = rt.stdout();
    trace!("Got output: {:?}", stdout);


    rt.ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
    rt.ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .iter()
        .filter(|id| collection_filter.filter(id))
        .for_each(|id| {
            rt.report_touched(&id).unwrap_or_exit();
        .map(|id| {
            if !rt.output_is_pipe() {
                let id = id.to_str().map_err_trace_exit_unwrap();
                let id = id.to_str()?;
                trace!("Writing to {:?}", stdout);
                writeln!(stdout, "{}", id).to_exit_code().unwrap_or_exit();
                writeln!(stdout, "{}", id)?;
            }

            rt.report_touched(&id).map_err(Error::from)
        })
        .collect()
}


M bin/core/imag-ids/Cargo.toml => bin/core/imag-ids/Cargo.toml +1 -0
@@ 24,6 24,7 @@ log        = "0.4.6"
toml       = "0.5.1"
toml-query = "0.9.2"
failure    = "0.1.5"
resiter    = "0.3.0"

libimagstore     = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-ids/src/lib.rs => bin/core/imag-ids/src/lib.rs +37 -43
@@ 39,6 39,7 @@ extern crate clap;
extern crate toml;
extern crate toml_query;
#[macro_use] extern crate failure;
extern crate resiter;

#[cfg(test)]
extern crate env_logger;


@@ 48,18 49,17 @@ extern crate libimagstore;
extern crate libimagrt;

use std::io::Write;
use std::result::Result as RResult;

use failure::Fallible as Result;
use failure::err_msg;
use failure::Error;
use resiter::Map;
use resiter::AndThen;
use clap::App;

use libimagstore::storeid::StoreId;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;

mod ui;



@@ 72,48 72,42 @@ impl ImagApplication for ImagIds {
    fn run(rt: Runtime) -> Result<()> {
        let print_storepath = rt.cli().is_present("print-storepath");

        let iterator = if rt.ids_from_stdin() {
            debug!("Fetching IDs from stdin...");
            let ids = rt
                .ids::<crate::ui::PathProvider>()
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| {
                    error!("No ids supplied");
                    ::std::process::exit(1);
                });
            Box::new(ids.into_iter().map(Ok))
                as Box<dyn Iterator<Item = RResult<StoreId, _>>>
        } else {
            Box::new(rt.store().entries().map_err_trace_exit_unwrap())
                as Box<dyn Iterator<Item = RResult<StoreId, _>>>
        }
        .trace_unwrap_exit()
            .map(|id| if print_storepath {
                (Some(rt.store().path()), id)
            } else {
                (None, id)
            });

        let mut stdout = rt.stdout();
        trace!("Got output: {:?}", stdout);

        iterator.for_each(|(storepath, id)| {
            rt.report_touched(&id).unwrap_or_exit();
            if !rt.output_is_pipe() {
                let id = id.to_str().map_err_trace_exit_unwrap();
                trace!("Writing to {:?}", stdout);

                let result = if let Some(store) = storepath {
                    writeln!(stdout, "{}/{}", store.display(), id)
                } else {
                    writeln!(stdout, "{}", id)
                };

                result.to_exit_code().unwrap_or_exit();
            }
        });
        let mut process = |iter: &mut dyn Iterator<Item = Result<StoreId>>| -> Result<()> {
            iter.map_ok(|id| if print_storepath {
                (Some(rt.store().path()), id)
            } else {
                (None, id)
            }).and_then_ok(|(storepath, id)| {
                if !rt.output_is_pipe() {
                    let id = id.to_str()?;
                    trace!("Writing to {:?}", stdout);

                    if let Some(store) = storepath {
                        writeln!(stdout, "{}/{}", store.display(), id)?;
                    } else {
                        writeln!(stdout, "{}", id)?;
                    }
                }

                rt.report_touched(&id).map_err(Error::from)
            })
            .collect::<Result<()>>()
        };

        if rt.ids_from_stdin() {
            debug!("Fetching IDs from stdin...");
            let mut iter = rt.ids::<crate::ui::PathProvider>()?
                .ok_or_else(|| err_msg("No ids supplied"))?
                .into_iter()
                .map(Ok);

        Ok(())
            process(&mut iter)
        } else {
            process(&mut rt.store().entries()?)
        }
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {

M bin/core/imag-init/src/bin.rs => bin/core/imag-init/src/bin.rs +1 -2
@@ 41,6 41,5 @@ extern crate libimaginitcmd;


fn main() -> Result<()> {
    libimaginitcmd::imag_init();
    Ok(())
    libimaginitcmd::imag_init()
}

M bin/core/imag-init/src/lib.rs => bin/core/imag-init/src/lib.rs +64 -84
@@ 35,7 35,8 @@
)]

extern crate clap;
extern crate failure;
#[macro_use] extern crate failure;

#[cfg(test)]
extern crate toml;



@@ 50,12 51,14 @@ use std::path::PathBuf;
use std::path::Path;
use std::process::Command;

use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use failure::err_msg;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;

use failure::Fallible as Result;
use clap::App;

const CONFIGURATION_STR : &str = include_str!("../imagrc.toml");


@@ 100,7 103,7 @@ impl ImagApplication for ImagInit {
    }
}

pub fn imag_init() {
pub fn imag_init() -> Result<()> {
    let version = make_imag_version!();
    let app     = ui::build_ui(Runtime::get_default_cli_builder(
        "imag-init",


@@ 109,35 112,27 @@ pub fn imag_init() {
    let matches = app.get_matches();
    let mut out = ::std::io::stdout();

    let path = matches
        .value_of("path")
        .map(String::from)
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            ::std::env::var("HOME")
                .map(PathBuf::from)
                .map(|mut p| { p.push(".imag"); p })
                .map(|path| if path.exists() {
                    writeln!(out, "Path '{:?}' already exists!", path)
                        .to_exit_code()
                        .unwrap_or_exit();
                    writeln!(out, "Cannot continue.")
                        .to_exit_code()
                        .unwrap_or_exit();
                    ::std::process::exit(1)
                } else {
                    path
                })
                .expect("Failed to retrieve/build path for imag directory.")
        });
    let path = if let Some(p) = matches.value_of("path") {
        PathBuf::from(String::from(p))
    } else {
        ::std::env::var("HOME")
            .map_err(Error::from)
            .map(PathBuf::from)
            .map(|mut p| { p.push(".imag"); p })
            .and_then(|path| if path.exists() {
                Err(format_err!("Cannot continue: Path '{}' already exists", path.display()))
            } else {
                Ok(path)
            })
            .map_err(|_| err_msg("Failed to retrieve/build path for imag directory."))?
    };

    {
        let mut store_path = path.clone();
        store_path.push("store");
        println!("Creating {}", store_path.display());

        ::std::fs::create_dir_all(store_path)
            .expect("Failed to create directory");
        ::std::fs::create_dir_all(store_path).context("Failed to create directory")?;
    }

    let config_path = {


@@ 146,11 141,12 @@ pub fn imag_init() {
        config_path
    };

    OpenOptions::new()
    let _ = OpenOptions::new()
        .write(true)
        .create(true)
        .open(config_path)
        .map(|mut f| {
        .map_err(Error::from)
        .and_then(|mut f| {
            let content = if matches.is_present("devel") {
                get_config_devel()
            } else {


@@ 158,33 154,34 @@ pub fn imag_init() {
            };

            f.write_all(content.as_bytes())
                .expect("Failed to write complete config to file");
                .context("Failed to write complete config to file")
                .map_err(Error::from)
        })
        .expect("Failed to open new configuration file");
        .context("Failed to open new configuration file")?;

    if find_command("git").is_some() && !matches.is_present("nogit") {
        // we initialize a git repository
        writeln!(out, "Going to initialize a git repository in the imag directory...")
            .to_exit_code()
            .unwrap_or_exit();
        writeln!(out, "Going to initialize a git repository in the imag directory...")?;

        let gitignore_path = {
            let mut gitignore_path = path.clone();
            gitignore_path.push(".gitignore");
            gitignore_path.to_str().map(String::from).expect("Cannot convert path to string")
        };
            gitignore_path.to_str().map(String::from)
        }.ok_or_else(|| err_msg("Cannot convert path to string"))?;

        OpenOptions::new()
        let _ = OpenOptions::new()
            .write(true)
            .create(true)
            .open(gitignore_path.clone())
            .map(|mut f| {
            .map_err(Error::from)
            .and_then(|mut f| {
                f.write_all(GITIGNORE_STR.as_bytes())
                    .expect("Failed to write complete gitignore to file");
                    .context("Failed to write complete gitignore to file")
                    .map_err(Error::from)
            })
            .expect("Failed to open new configuration file");
            .context("Failed to open new configuration file")?;

        let path_str = path.to_str().map(String::from).expect("Cannot convert path to string");
        let path_str = path.to_str().map(String::from).ok_or_else(|| err_msg("Cannot convert path to string"))?;
        let worktree = format!("--work-tree={}", path_str);
        let gitdir   = format!("--git-dir={}/.git", path_str);



@@ 192,20 189,16 @@ pub fn imag_init() {
            let output = Command::new("git")
                .args(&[&worktree, &gitdir, "--no-pager", "init"])
                .output()
                .expect("Calling 'git init' failed");
                .context("Calling 'git init' failed")?;

            if output.status.success() {
                writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(out, "'git {} {} --no-pager init' succeeded", worktree, gitdir)
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))?;
                writeln!(out, "'git {} {} --no-pager init' succeeded", worktree, gitdir)?;
            } else {
                writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))
                    .to_exit_code()
                    .unwrap_or_exit();
                ::std::process::exit(output.status.code().unwrap_or(1));
                writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))?;
                if !output.status.success() {
                    return Err(err_msg("Failed to execute git command"));
                }
            }
        }



@@ 213,19 206,16 @@ pub fn imag_init() {
            let output = Command::new("git")
                .args(&[&worktree, &gitdir, "--no-pager", "add", &gitignore_path])
                .output()
                .expect("Calling 'git add' failed");
                .context("Calling 'git add' failed")?;

            if output.status.success() {
                writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(out, "'git {} {} --no-pager add {}' succeeded", worktree, gitdir, gitignore_path)
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))?;
                writeln!(out, "'git {} {} --no-pager add {}' succeeded", worktree, gitdir, gitignore_path)?;
            } else {
                writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))
                    .to_exit_code()
                    .unwrap_or_exit();
                ::std::process::exit(output.status.code().unwrap_or(1));
                writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))?;
                if !output.status.success() {
                    return Err(err_msg("Failed to execute git command"));
                }
            }
        }



@@ 233,34 223,24 @@ pub fn imag_init() {
            let output = Command::new("git")
                .args(&[&worktree, &gitdir, "--no-pager", "commit", &gitignore_path, "-m", "'Initial import'"])
                .output()
                .expect("Calling 'git commit' failed");
                .context("Calling 'git commit' failed")?;
            if output.status.success() {
                writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(out, "'git {} {} --no-pager commit {} -m 'Initial import'' succeeded", worktree, gitdir, gitignore_path)
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(out, "{}", String::from_utf8(output.stdout).expect("No UTF-8 output"))?;
                writeln!(out, "'git {} {} --no-pager commit {} -m 'Initial import'' succeeded", worktree, gitdir, gitignore_path)?;
            } else {
                writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))
                    .to_exit_code()
                    .unwrap_or_exit();
                ::std::process::exit(output.status.code().unwrap_or(1));
                writeln!(out, "{}", String::from_utf8(output.stderr).expect("No UTF-8 output"))?;
                if !output.status.success() {
                    return Err(err_msg("Failed to execute git command"));
                }
            }
        }

        writeln!(out, "git stuff finished!")
            .to_exit_code()
            .unwrap_or_exit();
        writeln!(out, "git stuff finished!")?;
    } else {
        writeln!(out, "No git repository will be initialized")
            .to_exit_code()
            .unwrap_or_exit();
        writeln!(out, "No git repository will be initialized")?;
    }

    writeln!(out, "Ready. Have fun with imag!")
        .to_exit_code()
        .unwrap_or_exit();
    writeln!(out, "Ready. Have fun with imag!").map_err(Error::from)
}

fn get_config() -> String {

M bin/core/imag-link/src/lib.rs => bin/core/imag-link/src/lib.rs +117 -197
@@ 37,7 37,7 @@
#[macro_use] extern crate log;
extern crate clap;
extern crate url;
extern crate failure;
#[macro_use] extern crate failure;
#[macro_use] extern crate prettytable;
#[cfg(test)] extern crate toml;
#[cfg(test)] extern crate toml_query;


@@ 65,18 65,14 @@ use failure::err_msg;
use libimagentryurl::linker::UrlLinker;
use libimagentrylink::linkable::Linkable;
use libimagentrylink::storecheck::StoreLinkConsistentExt;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;
use libimagutil::warn_result::*;

use url::Url;
use failure::Fallible as Result;
use failure::Error;
use clap::App;

mod ui;


@@ 89,46 85,31 @@ pub enum ImagLink {}
impl ImagApplication for ImagLink {
    fn run(rt: Runtime) -> Result<()> {
        if rt.cli().is_present("check-consistency") {
            let exit_code = match rt.store().check_link_consistency() {
                Ok(_) => {
                    info!("Store is consistent");
                    0
                }
                Err(e) => {
                    trace_error(&e);
                    1
                }
            };
            ::std::process::exit(exit_code);
            rt.store().check_link_consistency()?;
            info!("Store is consistent");
        }

        let _ = rt.cli()
            .subcommand_name()
            .map(|name| {
                match name {
                    "remove" => remove_linking(&rt),
                    "unlink" => unlink(&rt),
                    "list"   => list_linkings(&rt),
                    other    => {
                        debug!("Unknown command");
                        let _ = rt.handle_unknown_subcommand("imag-link", other, rt.cli())
                            .map_err_trace_exit_unwrap()
                            .code()
                            .map(::std::process::exit);
                    },
                }
            })
            .or_else(|| {
                if let (Some(from), Some(to)) = (rt.cli().value_of("from"), rt.cli().values_of("to")) {
                    Some(link_from_to(&rt, from, to))
                } else {
                    warn_exit("No commandline call", 1)
                }
            })
            .ok_or_else(|| err_msg("No commandline call".to_owned()))
            .map_err_trace_exit_unwrap();

        Ok(())
        if let Some(name) = rt.cli().subcommand_name() {
            match name {
                "remove" => remove_linking(&rt),
                "unlink" => unlink(&rt),
                "list"   => list_linkings(&rt),
                other    => {
                    debug!("Unknown command");
                    if rt.handle_unknown_subcommand("imag-link", other, rt.cli())?.success() {
                        Ok(())
                    } else {
                        Err(format_err!("Subcommand failed"))
                    }
                },
            }
        } else {
            if let (Some(from), Some(to)) = (rt.cli().value_of("from"), rt.cli().values_of("to")) {
                link_from_to(&rt, from, to)
            } else {
                Err(err_msg("No commandline call"))
            }
        }
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 156,144 137,103 @@ fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockE
    result
}

fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I)
fn link_from_to<'a, I>(rt: &'a Runtime, from: &'a str, to: I) -> Result<()>
    where I: Iterator<Item = &'a str>
{
    let mut from_entry = match get_entry_by_name(rt, from).map_err_trace_exit_unwrap() {
        Some(e) => e,
        None    => {
            debug!("No 'from' entry");
            warn_exit("No 'from' entry", 1)
        },
    };
    let mut from_entry = get_entry_by_name(rt, from)?.ok_or_else(|| err_msg("No 'from' entry"))?;

    for entry in to {
        debug!("Handling 'to' entry: {:?}", entry);
        if rt.store().get(PathBuf::from(entry)).map_err_trace_exit_unwrap().is_none() {
        if rt.store().get(PathBuf::from(entry))?.is_none() {
            debug!("Linking externally: {:?} -> {:?}", from, entry);
            let url = Url::parse(entry).unwrap_or_else(|e| {
                error!("Error parsing URL: {:?}", e);
                ::std::process::exit(1);
            });
            let url = Url::parse(entry).map_err(|e| format_err!("Error parsing URL: {:?}", e))?;

            let iter = from_entry
                .add_url(rt.store(), url)
                .map_err_trace_exit_unwrap()
                .add_url(rt.store(), url)?
                .into_iter();

            rt.report_all_touched(iter).unwrap_or_exit();
            rt.report_all_touched(iter)?;
        } else {
            debug!("Linking internally: {:?} -> {:?}", from, entry);

            let from_id = StoreId::new(PathBuf::from(from)).map_err_trace_exit_unwrap();
            let entr_id = StoreId::new(PathBuf::from(entry)).map_err_trace_exit_unwrap();
            let from_id = StoreId::new(PathBuf::from(from))?;
            let entr_id = StoreId::new(PathBuf::from(entry))?;

            if from_id == entr_id {
                error!("Cannot link entry with itself. Exiting");
                ::std::process::exit(1)
                return Err(err_msg("Cannot link entry with itself. Exiting"))
            }

            let mut to_entry = match rt.store().get(entr_id).map_err_trace_exit_unwrap() {
                Some(e) => e,
                None    => {
                    warn!("No 'to' entry: {}", entry);
                    ::std::process::exit(1)
                },
            };
            from_entry
                .add_link(&mut to_entry)
                .map_err_trace_exit_unwrap();
            let mut to_entry = rt
                .store()
                .get(entr_id)?
                .ok_or_else(|| format_err!("No 'to' entry: {}", entry))?;

            rt.report_touched(to_entry.get_location()).unwrap_or_exit();
        }
            from_entry.add_link(&mut to_entry)?;

            rt.report_touched(to_entry.get_location())?;
        }

        info!("Ok: {} -> {}", from, entry);
    }

    rt.report_touched(from_entry.get_location()).unwrap_or_exit();
    rt.report_touched(from_entry.get_location()).map_err(Error::from)
}

fn remove_linking(rt: &Runtime) {
    let mut from = rt.cli()
fn remove_linking(rt: &Runtime) -> Result<()> {
    let mut from : FileLockEntry = rt.cli()
        .subcommand_matches("remove")
        .unwrap() // safe, we know there is an "remove" subcommand
        .value_of("from")
        .map(PathBuf::from)
        .map(|id| {
            rt.store()
                .get(id)
                .map_err_trace_exit_unwrap()
                .ok_or_else(|| warn_exit("No 'from' entry", 1))
                .unwrap() // safe by line above
        })
        .unwrap();
        .and_then(|id| rt.store().get(id).transpose())
        .ok_or_else(|| err_msg("No 'from' entry"))??;

    rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .for_each(|id| match rt.store().get(id.clone()) {
            Err(e) => trace_error(&e),
            Ok(Some(mut to_entry)) => {
                to_entry
                    .remove_link(&mut from)
                    .map_err_trace_exit_unwrap();

                rt.report_touched(to_entry.get_location()).unwrap_or_exit();
        .map(|id| match rt.store().get(id.clone())? {
            Some(mut to_entry) => {
                to_entry.remove_link(&mut from)?;
                rt.report_touched(to_entry.get_location()).map_err(Error::from)
            },
            Ok(None) => {

            None => {
                // looks like this is not an entry, but a filesystem URI and therefor an
                // external link...?
                if id.local().is_file() {
                    let pb = id.local().to_str().unwrap_or_else(|| {
                        warn!("Not StoreId and not a Path: {}", id);
                        ::std::process::exit(1);
                    });
                    let url = Url::parse(pb).unwrap_or_else(|e| {
                        error!("Error parsing URL: {:?}", e);
                        ::std::process::exit(1);
                    });
                    from.remove_url(rt.store(), url).map_err_trace_exit_unwrap();
                    let pb = id.local().to_str().ok_or_else(|| format_err!("Not StoreId and not a Path: {}", id))?;
                    let url = Url::parse(pb).map_err(|e| format_err!("Error parsing URL: {:?}", e))?;
                    from.remove_url(rt.store(), url)?;
                    info!("Ok: {}", id);
                    Ok(())
                } else {
                    warn!("Entry not found: {:?}", id);
                    Err(format_err!("Entry not found: {:?}", id))
                }
            }
        });
        })
        .collect::<Result<Vec<_>>>()?;

    rt.report_touched(from.get_location()).unwrap_or_exit();
    rt.report_touched(from.get_location()).map_err(Error::from)
}

fn unlink(rt: &Runtime) {
fn unlink(rt: &Runtime) -> Result<()> {
    rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
        .ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .for_each(|id| {
        .map(|id| {
            rt.store()
                .get(id.clone())
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| {
                    warn!("No entry for {}", id);
                    ::std::process::exit(1)
                })
                .unlink(rt.store())
                .map_err_trace_exit_unwrap();

            rt.report_touched(&id).unwrap_or_exit();
        });
                .get(id.clone())?
                .ok_or_else(|| format_err!("No entry for {}", id))?
                .unlink(rt.store())?;

            rt.report_touched(&id).map_err(Error::from)
        })
        .collect()
}

fn list_linkings(rt: &Runtime) {
fn list_linkings(rt: &Runtime) -> Result<()> {
    let cmd = rt.cli()
        .subcommand_matches("list")
        .unwrap(); // safed by clap


@@ 304,70 244,50 @@ fn list_linkings(rt: &Runtime) {
    let mut tab = ::prettytable::Table::new();
    tab.set_titles(row!["#", "Link"]);

    rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
    rt.ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .for_each(|id| {
            match rt.store().get(id.clone()) {
                Ok(Some(entry)) => {
                    for (i, link) in entry.links().map_err_trace_exit_unwrap().enumerate() {
                        let link = link
                            .to_str()
                            .map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
                            .ok();

                        if let Some(link) = link {
                            if list_plain {
                                writeln!(rt.stdout(), "{: <3}: {}", i, link)
                                    .to_exit_code()
                                    .unwrap_or_exit();
                            } else {
                                tab.add_row(row![i, link]);
                            }
                        }
                    }
        .map(|id| {
            let entry = rt.store().get(id.clone())?.ok_or_else(|| format_err!("Not found: {}", id))?;

                    if list_externals {
                        entry.get_urls(rt.store())
                            .map_err_trace_exit_unwrap()
                            .enumerate()
                            .for_each(|(i, link)| {
                                let link = link
                                    .map_err_trace_exit_unwrap()
                                    .into_string();

                                if list_plain {
                                    writeln!(rt.stdout(), "{: <3}: {}", i, link)
                                        .to_exit_code()
                                        .unwrap_or_exit();
                                } else {
                                    tab.add_row(row![i, link]);
                                }
                            })
                    }
            for (i, link) in entry.links()?.enumerate() {
                let link = link.to_str()?;

                    rt.report_touched(entry.get_location()).unwrap_or_exit();
                if list_plain {
                    writeln!(rt.stdout(), "{: <3}: {}", i, link)?;
                } else {
                    tab.add_row(row![i, link]);
                }
            }

                },
                Ok(None)        => warn!("Not found: {}", id),
                Err(e)          => trace_error(&e),
            if list_externals {
                entry.get_urls(rt.store())?
                    .enumerate()
                    .map(|(i, link)| {
                        let link = link?.into_string();

                        if list_plain {
                            writeln!(rt.stdout(), "{: <3}: {}", i, link)?;
                        } else {
                            tab.add_row(row![i, link]);
                        }

                        Ok(())
                    })
                    .collect::<Result<Vec<_>>>()?;
            }

            rt.report_touched(&id).unwrap_or_exit();
        });
            rt.report_touched(entry.get_location()).map_err(Error::from)
        })
        .collect::<Result<Vec<_>>>()?;

    if !list_plain {
        let out      = rt.stdout();
        let mut lock = out.lock();
        tab.print(&mut lock)
            .to_exit_code()
            .unwrap_or_exit();
        tab.print(&mut lock)?;
    }

    Ok(())
}

#[cfg(test)]


@@ 449,7 369,7 @@ mod tests {

        debug!("Entries created");

        link_from_to(&rt, "test1", vec!["test2"].into_iter());
        link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();

        debug!("Linking done");



@@ 480,7 400,7 @@ mod tests {

        debug!("Test entries created");

        link_from_to(&rt, "test1", vec!["test2"].into_iter());
        link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();

        debug!("Linking done");



@@ 509,8 429,8 @@ mod tests {

        debug!("Test entries created");

        link_from_to(&rt, "test1", vec!["test2"].into_iter());
        link_from_to(&rt, "test1", vec!["test2"].into_iter());
        link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();
        link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();

        debug!("Linking done");



@@ 540,8 460,8 @@ mod tests {

        debug!("Test entries created");

        link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter());
        link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter());
        link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()).unwrap();
        link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()).unwrap();

        debug!("Linking done");



@@ 576,14 496,14 @@ mod tests {

        debug!("Test entries created");

        link_from_to(&rt, "test1", vec!["test2"].into_iter());
        link_from_to(&rt, "test1", vec!["test2"].into_iter()).unwrap();

        debug!("Linking done");

        let rt = reset_test_runtime(vec!["remove", "test1", "test2"], rt)
            .unwrap();

        remove_linking(&rt);
        remove_linking(&rt).unwrap();

        debug!("Linking removed");



@@ 613,14 533,14 @@ mod tests {

        debug!("Test entries created");

        link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter());
        link_from_to(&rt, "test1", vec!["test2", "test3"].into_iter()).unwrap();

        debug!("linking done");

        let rt = reset_test_runtime(vec!["remove", "test1", "test2", "test3"], rt)
            .unwrap();

        remove_linking(&rt);
        remove_linking(&rt).unwrap();

        debug!("linking removed");


M bin/core/imag-markdown/Cargo.toml => bin/core/imag-markdown/Cargo.toml +1 -0
@@ 23,6 23,7 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log        = "0.4.6"
failure    = "0.1.5"
resiter    = "0.3.0"

libimagstore         = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt            = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-markdown/src/lib.rs => bin/core/imag-markdown/src/lib.rs +20 -26
@@ 37,6 37,7 @@
extern crate clap;
#[macro_use] extern crate log;
extern crate failure;
extern crate resiter;

extern crate libimagerror;
extern crate libimagrt;


@@ 47,13 48,14 @@ use std::io::Write;
use failure::Error;
use failure::err_msg;
use failure::Fallible as Result;
use resiter::AndThen;
use resiter::Map;
use clap::App;

use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use crate::libimagerror::iter::IterInnerOkOrElse;

mod ui;



@@ 69,40 71,32 @@ impl ImagApplication for ImagMarkdown {
        let mut outlock = out.lock();

        let iter = rt
            .ids::<crate::ui::PathProvider>()
            .map_err_trace_exit_unwrap()
            .unwrap_or_else(|| {
                error!("No ids supplied");
                ::std::process::exit(1);
            })
            .ids::<crate::ui::PathProvider>()?
            .ok_or_else(|| err_msg("No ids supplied"))?
            .into_iter()
            .map(Ok)
            .into_get_iter(rt.store())
            .trace_unwrap_exit()
            .map(|ofle| ofle.ok_or_else(|| {
                err_msg("Entry does not exist but is in store. This is a BUG, please report!")
            }))
            .trace_unwrap_exit();
            .map_inner_ok_or_else(|| err_msg("Entry does not exist but is in store. This is a BUG, please report!"));

        if only_links {
            iter.map(|fle| libimagentrymarkdown::link::extract_links(fle.get_content()))
                .for_each(|links| {
                    links.iter().for_each(|link| {
                        writeln!(outlock, "{title}: {link}", title = link.title, link = link.link)
                            .map_err(Error::from)
                            .map_err_trace_exit_unwrap();
                    })
            debug!("Printing only links");
            iter.map_ok(|fle| libimagentrymarkdown::link::extract_links(fle.get_content()))
                .and_then_ok(|links| {
                    links.iter()
                        .map(|link| {
                            writeln!(outlock, "{title}: {link}", title = link.title, link = link.link).map_err(Error::from)
                        })
                        .collect()
                })
                .collect()

        } else {
            iter.map(|fle| libimagentrymarkdown::html::to_html(fle.get_content()))
                .trace_unwrap_exit()
                .for_each(|html| {
                    writeln!(outlock, "{}", html).map_err(Error::from).map_err_trace_exit_unwrap();
            iter.and_then_ok(|fle| libimagentrymarkdown::html::to_html(fle.get_content()))
                .and_then_ok(|html| {
                    writeln!(outlock, "{}", html).map_err(Error::from).map_err(Error::from)
                })
                .collect()
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {

M bin/core/imag-mv/Cargo.toml => bin/core/imag-mv/Cargo.toml +1 -1
@@ 20,7 20,7 @@ is-it-maintained-open-issues      = { repository = "matthiasbeyer/imag" }
maintenance                       = { status     = "actively-developed" }

[dependencies]
log = "0.4.6"
log     = "0.4.6"
failure = "0.1.5"

libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-mv/src/lib.rs => bin/core/imag-mv/src/lib.rs +32 -60
@@ 35,33 35,29 @@
)]

#[macro_use] extern crate log;
#[macro_use] extern crate failure;
extern crate clap;
extern crate failure;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagentrylink;

use std::process::exit;

mod ui;

use std::path::PathBuf;
use std::result::Result as RResult;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::exit::ExitUnwrap;
use libimagstore::storeid::StoreId;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagentrylink::linkable::Linkable;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagerror::iter::IterInnerOkOrElse;

use failure::Fallible as Result;
use failure::err_msg;
use clap::App;




@@ 77,72 73,50 @@ impl ImagApplication for ImagMv {
            .value_of("source")
            .map(PathBuf::from)
            .map(StoreId::new)
            .unwrap() // unwrap safe by clap
            .map_err_trace_exit_unwrap();
            .unwrap()?; // unwrap safe by clap

        let destname = rt
            .cli()
            .value_of("dest")
            .map(PathBuf::from)
            .map(StoreId::new)
            .unwrap() // unwrap safe by clap
            .map_err_trace_exit_unwrap();
            .unwrap()?; // unwrap safe by clap

        // remove links to entry, and re-add them later
        let mut linked_entries = {
            rt.store()
                .get(sourcename.clone())
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| {
                    error!("Funny things happened: Entry moved to destination did not fail, but entry does not exist");
                    exit(1)
                })
                .links()
                .map_err_trace_exit_unwrap()
                .map(|link| Ok(link.get_store_id().clone()) as RResult<_, _>)
                .into_get_iter(rt.store())
                .trace_unwrap_exit()
                .map(|e| {
                    e.unwrap_or_else(|| {
                        error!("Linked entry does not exist");
                        exit(1)
                    })
                })
                .collect::<Vec<_>>()
        };
        let mut linked_entries = rt.store()
            .get(sourcename.clone())?
            .ok_or_else(|| format_err!("Entry does not exist: {}", sourcename))?
            .links()?
            .map(|link| link.get_store_id().clone())
            .map(Ok)
            .into_get_iter(rt.store())
            .map_inner_ok_or_else(|| err_msg("Linked entry does not exist"))
            .collect::<Result<Vec<_>>>()?;

        { // remove links to linked entries from source
            let mut entry = rt
                .store()
                .get(sourcename.clone())
                .map_err_trace_exit_unwrap()
                .unwrap_or_else(|| {
                    error!("Source Entry does not exist");
                    exit(1)
                });
                .get(sourcename.clone())?
                .ok_or_else(|| err_msg("Source Entry does not exist"))?;

            for link in linked_entries.iter_mut() {
                let _ = entry.remove_link(link).map_err_trace_exit_unwrap();
                entry.remove_link(link)?;
            }
        }

        let _ = rt
            .store()
            .move_by_id(sourcename.clone(), destname.clone())
            .map_err(|e| { // on error, re-add links
                debug!("Re-adding links to source entry because moving failed");
                relink(rt.store(), sourcename.clone(), &mut linked_entries);
                e
            })
            .map_err_trace_exit_unwrap();
        if let Err(e) = rt.store().move_by_id(sourcename.clone(), destname.clone()) {
            debug!("Re-adding links to source entry because moving failed");
            relink(rt.store(), sourcename.clone(), &mut linked_entries)?;

            return Err(e);
        }

        let _ = rt.report_touched(&destname).unwrap_or_exit();
        rt.report_touched(&destname)?;

        // re-add links to moved entry
        relink(rt.store(), destname, &mut linked_entries);
        relink(rt.store(), destname, &mut linked_entries)?;

        info!("Ok.");

        Ok(())
    }



@@ 165,17 139,15 @@ impl ImagApplication for ImagMv {



fn relink<'a>(store: &'a Store, target: StoreId, linked_entries: &mut Vec<FileLockEntry<'a>>) {
fn relink<'a>(store: &'a Store, target: StoreId, linked_entries: &mut Vec<FileLockEntry<'a>>) -> Result<()> {
    let mut entry = store
        .get(target)
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("Funny things happened: Entry moved to destination did not fail, but entry does not exist");
            exit(1)
        });

        .get(target)?
        .ok_or_else(|| err_msg("Funny things happened: Entry moved to destination did not fail, but entry does not exist"))?;

    for mut link in linked_entries {
        let _ = entry.add_link(&mut link).map_err_trace_exit_unwrap();
        let _ = entry.add_link(&mut link)?;
    }

    Ok(())
}


M bin/core/imag-ref/src/lib.rs => bin/core/imag-ref/src/lib.rs +56 -88
@@ 47,22 47,20 @@ extern crate libimagutil;

mod ui;

use std::process::exit;
use std::io::Write;

use failure::Error;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use clap::App;

use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagrt::application::ImagApplication;
use libimagrt::runtime::Runtime;
use libimagentryref::reference::Ref;
use libimagentryref::reference::MutRef;
use libimagentryref::reference::RefFassade;
use libimagentryref::hasher::default::DefaultHasher;
use libimagentryref::util::get_ref_config;
use libimagentryref::reference::MutRef;

/// Marker enum for implementing ImagApplication on
///


@@ 80,15 78,16 @@ impl ImagApplication for ImagRef {
                "list-dead" => list_dead(&rt),
                other => {
                    debug!("Unknown command");
                    let _ = rt.handle_unknown_subcommand("imag-ref", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .map(::std::process::exit);
                    if rt.handle_unknown_subcommand("imag-ref", other, rt.cli())?.success() {
                        Ok(())
                    } else {
                        Err(format_err!("Subcommand failed"))
                    }
                },
            }
        };

        Ok(())
        } else {
            Ok(())
        }
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 108,23 107,18 @@ impl ImagApplication for ImagRef {
    }
}

fn deref(rt: &Runtime) {
fn deref(rt: &Runtime) -> Result<()> {
    let cmd         = rt.cli().subcommand_matches("deref").unwrap();
    let basepath    = cmd.value_of("override-basepath");
    let cfg         = get_ref_config(&rt, "imag-ref").map_err_trace_exit_unwrap();
    let cfg         = get_ref_config(&rt, "imag-ref")?;
    let out         = rt.stdout();
    let mut outlock = out.lock();

    rt
        .ids::<::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
    rt.ids::<::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .for_each(|id| {
            match rt.store().get(id.clone()).map_err_trace_exit_unwrap() {
        .map(|id| {
            match rt.store().get(id.clone())? {
                Some(entry) => {
                    let r_entry = entry.as_ref_with_hasher::<DefaultHasher>();



@@ 132,89 126,63 @@ fn deref(rt: &Runtime) {
                        r_entry.get_path_with_basepath_setting(&cfg, alternative_basepath)
                    } else {
                        r_entry.get_path(&cfg)
                    }
                    .map_err_trace_exit_unwrap()
                    }?
                    .to_str()
                    .ok_or_else(|| ::libimagerror::errors::ErrorMsg::UTF8Error)
                    .map_err(Error::from)
                    .and_then(|s| writeln!(outlock, "{}", s).map_err(Error::from))
                    .map_err_trace_exit_unwrap();
                    .ok_or_else(|| Error::from(::libimagerror::errors::ErrorMsg::UTF8Error))
                    .and_then(|s| writeln!(outlock, "{}", s).map_err(Error::from))?;

                    rt.report_touched(&id).unwrap_or_exit();
                },
                None => {
                    error!("No entry for id '{}' found", id);
                    exit(1)
                    rt.report_touched(&id).map_err(Error::from)
                },
                None => Err(format_err!("No entry for id '{}' found", id))
            }
        });
        })
        .collect()
}

fn remove(rt: &Runtime) {
fn remove(rt: &Runtime) -> Result<()> {
    use libimaginteraction::ask::ask_bool;

    let cmd = rt.cli().subcommand_matches("remove").unwrap();
    let yes = cmd.is_present("yes");

    let mut input = rt.stdin().unwrap_or_else(|| {
        error!("No input stream. Cannot ask for permission");
        exit(1);
    });

    let cmd        = rt.cli().subcommand_matches("remove").unwrap();
    let yes        = cmd.is_present("yes");
    let mut input  = rt.stdin().ok_or_else(|| err_msg("No input stream. Cannot ask for permission"))?;
    let mut output = rt.stdout();

    rt
        .ids::<::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
    rt.ids::<::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .for_each(|id| {
            match rt.store().get(id.clone()).map_err_trace_exit_unwrap() {
        .map(|id| {
            match rt.store().get(id.clone())? {
                None            => Err(format_err!("No entry for id '{}' found", id)),
                Some(mut entry) => {
                    if yes ||
                        ask_bool(&format!("Delete ref from entry '{}'", id), None, &mut input, &mut output)
                            .map_err_trace_exit_unwrap()
                    {
                        entry.as_ref_with_hasher_mut::<DefaultHasher>()
                            .remove_ref()
                            .map_err_trace_exit_unwrap();
                    if yes || ask_bool(&format!("Delete ref from entry '{}'", id), None, &mut input, &mut output)?  {
                        entry.as_ref_with_hasher_mut::<DefaultHasher>().remove_ref()
                    } else {
                        info!("Aborted");
                        Ok(())
                    }
                },
                None => {
                    error!("No entry for id '{}' found", id);
                    exit(1)
                },
            }
        });
        })
        .collect()
}

fn list_dead(rt: &Runtime) {
    let cfg        = get_ref_config(&rt, "imag-ref").map_err_trace_exit_unwrap();
fn list_dead(rt: &Runtime) -> Result<()> {
    let cfg        = get_ref_config(&rt, "imag-ref")?;
    let cmd        = rt.cli().subcommand_matches("list-dead").unwrap(); // safe by main()
    let list_path  = cmd.is_present("list-dead-pathes");
    let list_id    = cmd.is_present("list-dead-ids");
    let mut output = rt.stdout();

    rt
        .ids::<crate::ui::PathProvider>()
        .map_err_trace_exit_unwrap()
        .unwrap_or_else(|| {
            error!("No ids supplied");
            ::std::process::exit(1);
        })
    rt.ids::<crate::ui::PathProvider>()?
        .ok_or_else(|| err_msg("No ids supplied"))?
        .into_iter()
        .for_each(|id| {
            match rt.store().get(id.clone()).map_err_trace_exit_unwrap() {
        .map(|id| {
            match rt.store().get(id.clone())? {
                Some(entry) => {
                    let entry_ref = entry.as_ref_with_hasher::<DefaultHasher>();

                    if entry_ref.is_ref().map_err_trace_exit_unwrap() { // we only care if the entry is a ref
                        let entry_path = entry_ref.get_path(&cfg).map_err_trace_exit_unwrap();
                    if entry_ref.is_ref()? { // we only care if the entry is a ref
                        let entry_path = entry_ref.get_path(&cfg)?;

                        if !entry_path.exists() {
                            if list_id {


@@ 223,24 191,24 @@ fn list_dead(rt: &Runtime) {
                                writeln!(output, "{}", entry_path.display())
                            } else {
                                unimplemented!()
                            }
                            .map_err(Error::from)
                            .map_err_trace_exit_unwrap();
                            }?;

                            rt.report_touched(entry.get_location()).unwrap_or_exit();
                            rt.report_touched(entry.get_location()).map_err(Error::from)
                        } else {
                            Ok(())
                        }
                    } else {
                        Ok(())
                    }
                }

                None => {
                    error!("Does not exist: {}", id.local().display());
                    exit(1)
                }
                None => Err(format_err!("Does not exist: {}", id.local().display())),
            }
        });
        })
        .collect()
}

fn create(_rt: &Runtime) {
fn create(_rt: &Runtime) -> Result<()> {
    unimplemented!()
}


M bin/core/imag-store/Cargo.toml => bin/core/imag-store/Cargo.toml +1 -0
@@ 23,6 23,7 @@ maintenance                       = { status     = "actively-developed" }
log = "0.4.6"
toml = "0.5.1"
failure = "0.1.5"
resiter = "0.3.0"

libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore", features = ["verify"] }
libimagrt    = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-store/src/create.rs => bin/core/imag-store/src/create.rs +10 -21
@@ 17,34 17,32 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::ops::DerefMut;
use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::io::Read;
use std::ops::DerefMut;

use clap::ArgMatches;
use toml::Value;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::storeid::StoreId;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagutil::debug_result::*;

use crate::util::build_toml_header;

pub fn create(rt: &Runtime) {
pub fn create(rt: &Runtime) -> Result<()> {
    let scmd = rt.cli().subcommand_matches("create").unwrap();
    debug!("Found 'create' subcommand...");

    // unwrap is safe as value is required
    let path  = scmd.value_of("path").unwrap();
    let path  = PathBuf::from(path);
    let path  = StoreId::new(path).map_err_trace_exit_unwrap();
    let path  = StoreId::new(path)?;

    debug!("path = {:?}", path);



@@ 55,15 53,13 @@ pub fn create(rt: &Runtime) {
            .or_else(|_| create_with_content_and_header(rt,
                                                        &path,
                                                        String::new(),
                                                        Entry::default_header()))
                                                        Entry::default_header()))?;
    } else {
        debug!("Creating entry");
        create_with_content_and_header(rt, &path, String::new(),
            Entry::default_header())
        create_with_content_and_header(rt, &path, String::new(), Entry::default_header())?;
    }
    .map_err_trace_exit_unwrap();

    rt.report_touched(&path).unwrap_or_exit();
    rt.report_touched(&path).map_err(Error::from)
}

fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> Result<()> {


@@ 99,19 95,13 @@ fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> Res
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])
        .map_dbg_err(|e| format!("Error building entry: {:?}", e))
        .and_then(|new_e| {
            let r = rt.store()
            rt.store()
                .create(path.clone())
                .map_dbg_err(|e| format!("Error in Store::create(): {:?}", e))
                .map(|mut old_e| {
                    *old_e.deref_mut() = new_e;
                });

            debug!("Entry build");
            r
                })
        })
        .map_dbg_err(|e| format!("Error storing entry: {:?}", e))
}

fn create_with_content_and_header(rt: &Runtime,


@@ 122,7 112,6 @@ fn create_with_content_and_header(rt: &Runtime,
    debug!("Creating entry with content at {:?}", path);
    rt.store()
        .create(path.clone())
        .map_dbg_err(|e| format!("Error in Store::create(): {:?}", e))
        .map(|mut element| {
            {
                let e_content = element.get_content_mut();


@@ 177,7 166,7 @@ mod tests {
        let test_name = "test_create_simple";
        let rt = generate_test_runtime(vec!["create", "test_create_simple"]).unwrap();

        create(&rt);
        create(&rt).unwrap();

        let e = rt.store().get(PathBuf::from(test_name));
        assert!(e.is_ok());

M bin/core/imag-store/src/delete.rs => bin/core/imag-store/src/delete.rs +7 -10
@@ 19,22 19,19 @@

use std::path::PathBuf;

use failure::Fallible as Result;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagstore::storeid::StoreId;
use libimagutil::warn_result::*;

pub fn delete(rt: &Runtime) {
pub fn delete(rt: &Runtime) -> Result<()> {
    let scmd  = rt.cli().subcommand_matches("delete").unwrap();
    let id    = scmd.value_of("id").unwrap(); // safe by clap
    let path  = PathBuf::from(id);
    let path  = StoreId::new(path).map_err_trace_exit_unwrap();
    let path  = StoreId::new(path)?;
    debug!("Deleting file at {:?}", id);

    rt.store()
        .delete(path)
        .map_warn_err(|e| format!("Error: {:?}", e))
        .map_err_trace_exit_unwrap();
    rt.store().delete(path)
}

#[cfg(test)]


@@ 59,11 56,11 @@ mod tests {
        let test_name = "test_create_simple";
        let rt = generate_test_runtime(vec!["create", "test_create_simple"]).unwrap();

        create(&rt);
        create(&rt).unwrap();

        let rt = reset_test_runtime(vec!["delete", "test_create_simple"], rt).unwrap();

        delete(&rt);
        delete(&rt).unwrap();

        let e = rt.store().get(PathBuf::from(test_name));
        assert!(e.is_ok());

M bin/core/imag-store/src/get.rs => bin/core/imag-store/src/get.rs +11 -9
@@ 19,27 19,29 @@

use std::path::PathBuf;

use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagstore::storeid::StoreId;

use crate::retrieve::print_entry;

pub fn get(rt: &Runtime) {
pub fn get(rt: &Runtime) -> Result<()> {
    let scmd = rt.cli().subcommand_matches("get").unwrap();

    let id    = scmd.value_of("id").unwrap(); // safe by clap
    let path  = PathBuf::from(id);
    let path  = StoreId::new(path).map_err_trace_exit_unwrap();
    let path  = StoreId::new(path)?;
    debug!("path = {:?}", path);

    match rt.store().get(path.clone()).map_err_trace_exit_unwrap() {
    match rt.store().get(path.clone())? {
        None        => Err(err_msg("No entry found")),
        Some(entry) => {
            print_entry(rt, scmd, entry);
            rt.report_touched(&path).unwrap_or_exit();
            print_entry(rt, scmd, entry)?;
            rt.report_touched(&path).map_err(Error::from)
        },
        None        => info!("No entry found"),
    };
    }
}


M bin/core/imag-store/src/lib.rs => bin/core/imag-store/src/lib.rs +13 -14
@@ 37,8 37,9 @@
extern crate clap;
#[macro_use] extern crate log;
extern crate toml;
extern crate resiter;
#[cfg(test)] extern crate toml_query;
extern crate failure;
#[macro_use] extern crate failure;

extern crate libimagrt;
extern crate libimagstore;


@@ 53,7 54,9 @@ extern crate libimagutil;

use libimagrt::application::ImagApplication;
use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;

use failure::Fallible as Result;
use failure::err_msg;

mod create;
mod delete;


@@ 66,7 69,6 @@ mod util;

use std::ops::Deref;

use failure::Fallible as Result;
use clap::App;

use crate::create::create;


@@ 83,9 85,7 @@ use crate::verify::verify;
pub enum ImagStore {}
impl ImagApplication for ImagStore {
    fn run(rt: Runtime) -> Result<()> {
        let command = rt.cli().subcommand_name().map(String::from);

        if let Some(command) = command {
        if let Some(command) = rt.cli().subcommand_name() {
            debug!("Call: {}", command);
            match command.deref() {
                "create"   => create(&rt),


@@ 96,17 96,16 @@ impl ImagApplication for ImagStore {
                "verify"   => verify(&rt),
                other      => {
                    debug!("Unknown command");
                    let _ = rt.handle_unknown_subcommand("imag-store", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .map(::std::process::exit);
                    if rt.handle_unknown_subcommand("imag-store", other, rt.cli())?.success() {
                        Ok(())
                    } else {
                        Err(format_err!("Subcommand failed"))
                    }
                },
            };
            }
        } else {
            debug!("No command");
            Err(err_msg("No command"))
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {

M bin/core/imag-store/src/retrieve.rs => bin/core/imag-store/src/retrieve.rs +18 -30
@@ 20,41 20,32 @@
use std::path::PathBuf;
use std::io::Write;

use failure::Fallible as Result;
use failure::Error;
use clap::ArgMatches;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagutil::debug_result::*;

pub fn retrieve(rt: &Runtime) {
    if let Some(scmd) = rt.cli().subcommand_matches("retrieve") {
        // unwrap() is safe as arg is required
        let id    = scmd.value_of("id").unwrap();
        let path  = PathBuf::from(id);
        let path  = StoreId::new(path).map_err_trace_exit_unwrap();
        debug!("path = {:?}", path);
pub fn retrieve(rt: &Runtime) -> Result<()> {
    let scmd  = rt.cli().subcommand_matches("retrieve").unwrap();
    let id    = scmd.value_of("id").unwrap();
    let path  = PathBuf::from(id);
    let path  = StoreId::new(path)?;
    debug!("path = {:?}", path);

        rt.store()
            .retrieve(path.clone())
            .map(|e| print_entry(rt, scmd, e))
            .map_dbg_str("No entry")
            .map_dbg(|e| format!("{:?}", e))
            .map_err_trace_exit_unwrap();
    rt.store()
        .retrieve(path.clone())
        .and_then(|e| print_entry(rt, scmd, e))?;

        rt.report_touched(&path).unwrap_or_exit();
    }
    rt.report_touched(&path).map_err(Error::from)
}

pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) {
pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) -> Result<()> {
    if do_print_raw(scmd) {
        debug!("Printing raw content...");
        writeln!(rt.stdout(), "{}", e.to_str().map_err_trace_exit_unwrap())
            .to_exit_code()
            .unwrap_or_exit();
        writeln!(rt.stdout(), "{}", e.to_str()?)?;
    } else if do_filter(scmd) {
        debug!("Filtering...");
        warn!("Filtering via header specs is currently now supported.");


@@ 71,20 62,17 @@ pub fn print_entry(rt: &Runtime, scmd: &ArgMatches, e: FileLockEntry) {
                unimplemented!()
            } else {
                debug!("Printing header as TOML...");
                writeln!(rt.stdout(), "{}", e.get_header())
                    .to_exit_code()
                    .unwrap_or_exit();
                writeln!(rt.stdout(), "{}", e.get_header())?;
            }
        }

        if do_print_content(scmd) {
            debug!("Printing content...");
            writeln!(rt.stdout(), "{}", e.get_content())
                    .to_exit_code()
                    .unwrap_or_exit();
            writeln!(rt.stdout(), "{}", e.get_content())?;
        }

    }

    Ok(())
}

fn do_print_header(m: &ArgMatches) -> bool {

M bin/core/imag-store/src/update.rs => bin/core/imag-store/src/update.rs +9 -8
@@ 20,22 20,23 @@
use std::ops::DerefMut;
use std::path::PathBuf;

use failure::Fallible as Result;
use failure::Error;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagstore::storeid::StoreId;

use crate::util::build_toml_header;

pub fn update(rt: &Runtime) {
pub fn update(rt: &Runtime) -> Result<()> {
    let scmd  = rt.cli().subcommand_matches("update").unwrap();
    let id    = scmd.value_of("id").unwrap(); // Safe by clap
    let path  = PathBuf::from(id);
    let path  = StoreId::new(path).map_err_trace_exit_unwrap();
    let path  = StoreId::new(path)?;

    let _ = rt.store()
    rt.store()
        .retrieve(path)
        .map(|mut locked_e| {
        .and_then(|mut locked_e| {
            {
                let e = locked_e.deref_mut();



@@ 48,7 49,7 @@ pub fn update(rt: &Runtime) {
                debug!("New header set");
            }

            rt.report_touched(locked_e.get_location()).unwrap_or_exit();
        });
            rt.report_touched(locked_e.get_location()).map_err(Error::from)
        })
}


M bin/core/imag-store/src/verify.rs => bin/core/imag-store/src/verify.rs +17 -14
@@ 19,26 19,25 @@

use std::ops::Deref;

use failure::Fallible as Result;
use failure::err_msg;
use resiter::AndThen;

use libimagrt::runtime::Runtime;
use libimagutil::warn_exit::warn_exit;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::iter::TraceIterator;
use libimagerror::iter::IterInnerOkOrElse;

/// Verify the store.
///
/// This function is not intended to be called by normal programs but only by `imag-store`.
pub fn verify(rt: &Runtime) {
pub fn verify(rt: &Runtime) -> Result<()> {
    info!("Header | Content length | Path");
    info!("-------+----------------+-----");
    let result = rt
        .store()
        .entries()
        .map_err_trace_exit_unwrap()
        .entries()?
        .into_get_iter()
        .trace_unwrap_exit()
        .filter_map(|x| x)
        .all(|fle| {
        .map_inner_ok_or_else(|| err_msg("Did not find one entry"))
        .and_then_ok(|fle| {
            let p           = fle.get_location();
            let content_len = fle.get_content().len();
            let (verify, status) = if fle.verify().is_ok() {


@@ 48,14 47,18 @@ pub fn verify(rt: &Runtime) {
            };

            info!("{: >6} | {: >14} | {:?}", verify, content_len, p.deref());
            rt.report_touched(fle.get_location()).unwrap_or_exit();
            status
        });
            rt.report_touched(fle.get_location())?;
            Ok(status)
        })
        .collect::<Result<Vec<_>>>()?
        .iter()
        .all(|x| *x);

    if result {
        info!("Store seems to be fine");
        Ok(())
    } else {
        warn_exit("Store seems to be broken somehow", 1);
        Err(err_msg("Store seems to be broken somehow"))
    }
}


M bin/core/imag-tag/src/lib.rs => bin/core/imag-tag/src/lib.rs +70 -97
@@ 38,7 38,7 @@ extern crate clap;
#[macro_use] extern crate log;

#[cfg(test)] extern crate toml;
extern crate failure;
#[macro_use] extern crate failure;

extern crate libimagstore;
extern crate libimagrt;


@@ 60,19 60,17 @@ extern crate env_logger;

use std::io::Write;

use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagentrytag::tagable::Tagable;
use libimagentrytag::tag::Tag;
use libimagerror::trace::trace_error;
use libimagerror::trace::MapErrTrace;
use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;

use clap::{App, ArgMatches};
use failure::Fallible as Result;

mod ui;



@@ 84,43 82,40 @@ mod ui;
pub enum ImagTag {}
impl ImagApplication for ImagTag {
    fn run(rt: Runtime) -> Result<()> {
        let ids = rt
            .ids::<crate::ui::PathProvider>()
            .map_err_trace_exit_unwrap()
            .unwrap_or_else(|| {
                error!("No ids supplied");
                ::std::process::exit(1);
            })
        let ids = rt.ids::<crate::ui::PathProvider>()?
            .ok_or_else(|| err_msg("No ids supplied"))?
            .into_iter();

        if let Some(name) = rt.cli().subcommand_name() {
            match name {
                "list" => for id in ids {
                    list(id, &rt)
                },
                "remove" => for id in ids {
                "list" => ids.into_iter().map(|id| list(id, &rt)).collect(),

                "remove" => ids.into_iter().map(|id| {
                    let add = None;
                    let rem = get_remove_tags(rt.cli());
                    let rem = get_remove_tags(rt.cli())?;
                    debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
                    alter(&rt, id, add, rem);
                },
                "add" => for id in ids {
                    let add = get_add_tags(rt.cli());
                    alter(&rt, id, add, rem)
                }).collect(),

                "add" => ids.into_iter().map(|id| {
                    let add = get_add_tags(rt.cli())?;
                    let rem = None;
                    debug!("id = {:?}, add = {:?}, rem = {:?}", id, add, rem);
                    alter(&rt, id, add, rem);
                },
                    alter(&rt, id, add, rem)
                }).collect(),

                other => {
                    debug!("Unknown command");
                    let _ = rt.handle_unknown_subcommand("imag-tag", other, rt.cli())
                        .map_err_trace_exit_unwrap()
                        .code()
                        .map(::std::process::exit);
                    if rt.handle_unknown_subcommand("imag-tag", other, rt.cli())?.success() {
                        Ok(())
                    } else {
                        Err(format_err!("Subcommand failed"))
                    }
                },
            }
        } else {
            Ok(())
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 140,63 135,46 @@ impl ImagApplication for ImagTag {
    }
}

fn alter(rt: &Runtime, path: StoreId, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) {
    match rt.store().get(path.clone()) {
        Ok(Some(mut e)) => {
fn alter(rt: &Runtime, path: StoreId, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) -> Result<()> {
    match rt.store().get(path.clone())? {
        Some(mut e) => {
            debug!("Entry header now = {:?}", e.get_header());

            if let Some(tags) = add {
                debug!("Adding tags = '{:?}'", tags);
                for tag in tags {
                tags.into_iter().map(|tag| {
                    debug!("Adding tag '{:?}'", tag);
                    if let Err(e) = e.add_tag(tag) {
                        trace_error(&e);
                    } else {
                        debug!("Adding tag worked");
                    }
                }
                    e.add_tag(tag)
                }).collect::<Result<Vec<_>>>()?;
            } // it is okay to ignore a None here

            debug!("Entry header now = {:?}", e.get_header());

            if let Some(tags) = rem {
                debug!("Removing tags = '{:?}'", tags);
                for tag in tags {
                tags.into_iter().map(|tag| {
                    debug!("Removing tag '{:?}'", tag);
                    if let Err(e) = e.remove_tag(tag) {
                        trace_error(&e);
                    }
                }
                    e.remove_tag(tag)
                }).collect::<Result<Vec<_>>>()?;
            } // it is okay to ignore a None here

            debug!("Entry header now = {:?}", e.get_header());

        },

        Ok(None) => {
        None => {
            info!("No entry found.");
        },

        Err(e) => {
            info!("No entry.");
            trace_error(&e);
        },
    }

    rt.report_touched(&path).unwrap_or_exit();
    rt.report_touched(&path).map_err(Error::from)
}

fn list(path: StoreId, rt: &Runtime) {
    let entry = match rt.store().get(path.clone()).map_err_trace_exit_unwrap() {
        Some(e) => e,
        None => warn_exit("No entry found.", 1),
    };

    let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe, we checked in main()

    let json_out = scmd.is_present("json");
    let line_out = scmd.is_present("linewise");
    let sepp_out = scmd.is_present("sep");
fn list(path: StoreId, rt: &Runtime) -> Result<()> {
    let entry        = rt.store().get(path.clone())?.ok_or_else(|| err_msg("No entry found"))?;
    let scmd         = rt.cli().subcommand_matches("list").unwrap(); // safe, we checked in main()
    let json_out     = scmd.is_present("json");
    let line_out     = scmd.is_present("linewise");
    let sepp_out     = scmd.is_present("sep");
    let mut comm_out = scmd.is_present("commasep");

    if !vec![json_out, line_out, comm_out, sepp_out].iter().any(|v| *v) {


@@ 204,7 182,7 @@ fn list(path: StoreId, rt: &Runtime) {
        comm_out = true;
    }

    let tags = entry.get_tags().map_err_trace_exit_unwrap();
    let tags = entry.get_tags()?;

    if json_out {
        unimplemented!()


@@ 212,53 190,44 @@ fn list(path: StoreId, rt: &Runtime) {

    if line_out {
        for tag in &tags {
            writeln!(rt.stdout(), "{}", tag)
                .to_exit_code()
                .unwrap_or_exit();
            writeln!(rt.stdout(), "{}", tag)?;
        }
    }

    if sepp_out {
        let sepp = scmd.value_of("sep").unwrap(); // we checked before
        writeln!(rt.stdout(), "{}", tags.join(sepp))
            .to_exit_code()
            .unwrap_or_exit();
        writeln!(rt.stdout(), "{}", tags.join(sepp))?;
    }

    if comm_out {
        writeln!(rt.stdout(), "{}", tags.join(", "))
            .to_exit_code()
            .unwrap_or_exit();
        writeln!(rt.stdout(), "{}", tags.join(", "))?;
    }

    rt.report_touched(&path).unwrap_or_exit();
    rt.report_touched(&path).map_err(Error::from)
}

/// Get the tags which should be added from the commandline
///
/// Returns none if the argument was not specified
fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
fn get_add_tags(matches: &ArgMatches) -> Result<Option<Vec<Tag>>> {
    retrieve_tags(matches, "add", "add-tags")
}

/// Get the tags which should be removed from the commandline
///
/// Returns none if the argument was not specified
fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
fn get_remove_tags(matches: &ArgMatches) -> Result<Option<Vec<Tag>>> {
    retrieve_tags(matches, "remove", "remove-tags")
}

fn retrieve_tags(m: &ArgMatches, s: &'static str, v: &'static str) -> Option<Vec<Tag>> {
    Some(m
fn retrieve_tags(m: &ArgMatches, s: &'static str, v: &'static str) -> Result<Option<Vec<Tag>>> {
    Ok(Some(m
         .subcommand_matches(s)
         .unwrap_or_else(|| {
             error!("Expected subcommand '{}', but was not specified", s);
             ::std::process::exit(1)
         })
         .ok_or_else(|| format_err!("Expected subcommand '{}', but was not specified", s))?
         .values_of(v)
         .unwrap() // enforced by clap
         .map(String::from)
         .collect())
         .collect()))
}

#[cfg(test)]


@@ 314,7 283,7 @@ mod tests {
    }

    #[test]
    fn test_tag_add_adds_tag() {
    fn test_tag_add_adds_tag() -> Result<()> {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-add-adds-tags";


@@ 325,11 294,11 @@ mod tests {
        let id = PathBuf::from(String::from(name));

        debug!("Getting 'add' tags");
        let add = get_add_tags(rt.cli());
        let add = get_add_tags(rt.cli())?;
        debug!("Add-tags: {:?}", add);

        debug!("Altering things");
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, None);
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, None)?;
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();


@@ 343,10 312,11 @@ mod tests {

        assert_ne!(*test_tags, tags_toml_value(vec![]));
        assert_eq!(*test_tags, tags_toml_value(vec!["foo"]));
        Ok(())
    }

    #[test]
    fn test_tag_remove_removes_tag() {
    fn test_tag_remove_removes_tag() -> Result<()> {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-tag";


@@ 360,21 330,22 @@ mod tests {
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        let rem = get_remove_tags(rt.cli())?;
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, rem);
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, rem)?;
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
        Ok(())
    }

    #[test]
    fn test_tag_remove_removes_only_to_remove_tag() {
    fn test_tag_remove_removes_only_to_remove_tag() -> Result<()> {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-only-to-remove-tag-doesnt-crash-on-nonexistent-tag";


@@ 388,21 359,22 @@ mod tests {
        let add = Some(vec![ "foo".to_owned(), "bar".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        let rem = get_remove_tags(rt.cli())?;
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, rem);
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, rem)?;
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bar"]));
        Ok(())
    }

    #[test]
    fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() {
    fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() -> Result<()> {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-but-doesnt-crash-on-nonexistent-tag";


@@ 416,17 388,18 @@ mod tests {
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        let rem = get_remove_tags(rt.cli())?;
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, rem);
        alter(&rt, StoreId::new(id.clone()).unwrap(), add, rem)?;
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
        Ok(())
    }

}

M bin/core/imag-view/Cargo.toml => bin/core/imag-view/Cargo.toml +1 -0
@@ 26,6 26,7 @@ toml-query = "0.9.2"
handlebars = "2"
tempfile = "3.0.9"
failure = "0.1.5"
resiter = "0.3.0"

libimagstore     = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagrt        = { version = "0.10.0", path = "../../../lib/core/libimagrt" }

M bin/core/imag-view/src/lib.rs => bin/core/imag-view/src/lib.rs +56 -105
@@ 40,7 40,8 @@ extern crate handlebars;
extern crate tempfile;
extern crate toml;
extern crate toml_query;
extern crate failure;
#[macro_use] extern crate failure;
extern crate resiter;

extern crate libimagentryview;
extern crate libimagerror;


@@ 52,26 53,23 @@ use std::str::FromStr;
use std::collections::BTreeMap;
use std::io::Write;
use std::process::Command;
use std::process::exit;

use handlebars::Handlebars;
use toml_query::read::TomlValueReadTypeExt;
use failure::Error;
use failure::err_msg;
use failure::Fallible as Result;
use resiter::AndThen;
use clap::App;

use libimagrt::runtime::Runtime;
use libimagrt::application::ImagApplication;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagentryview::builtin::stdout::StdoutViewer;
use libimagentryview::builtin::md::MarkdownViewer;
use libimagentryview::viewer::Viewer;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::store::FileLockEntry;
use libimagerror::iter::IterInnerOkOrElse;

mod ui;



@@ 85,61 83,42 @@ impl ImagApplication for ImagView {
        let view_header  = rt.cli().is_present("view-header");
        let hide_content = rt.cli().is_present("not-view-content");
        let entries      = rt
            .ids::<::ui::PathProvider>()
            .map_err_trace_exit_unwrap()
            .unwrap_or_else(|| {
                error!("No ids supplied");
                ::std::process::exit(1);
            })
            .ids::<::ui::PathProvider>()?
            .ok_or_else(|| err_msg("No ids supplied"))?
            .into_iter()
            .map(Ok)
            .into_get_iter(rt.store())
            .trace_unwrap_exit()
            .map(|e| {
                e.ok_or_else(|| err_msg("Entry not found"))
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap()
            });
            .map_inner_ok_or_else(|| err_msg("Entry not found, please report this as a bug"));

        if rt.cli().is_present("in") {
            let files = entries
                .map(|entry| {
                    let tmpfile = create_tempfile_for(&entry, view_header, hide_content);
                    rt.report_touched(entry.get_location()).unwrap_or_exit();
                    tmpfile
                .and_then_ok(|entry| {
                    let tmpfile = create_tempfile_for(&entry, view_header, hide_content)?;
                    rt.report_touched(entry.get_location())?;
                    Ok(tmpfile)
                })
                .collect::<Vec<_>>();
                .collect::<Result<Vec<_>>>()?;

            let mut command = {
                let viewer = rt
                    .cli()
                    .value_of("in")
                    .ok_or_else(|| Error::from(err_msg("No viewer given")))
                    .map_err_trace_exit_unwrap();
                    .ok_or_else(|| err_msg("No viewer given"))?;

                let config = rt
                    .config()
                    .ok_or_else(|| Error::from(err_msg("No configuration, cannot continue")))
                    .map_err_trace_exit_unwrap();
                    .ok_or_else(|| err_msg("No configuration, cannot continue"))?;

                let query = format!("view.viewers.{}", viewer);

                let viewer_template = config
                    .read_string(&query)
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap()
                    .unwrap_or_else(|| {
                        error!("Cannot find '{}' in config", query);
                        exit(1)
                    });
                    .read_string(&query)?
                    .ok_or_else(|| format_err!("Cannot find '{}' in config", query))?;

                let mut handlebars = Handlebars::new();
                handlebars.register_escape_fn(::handlebars::no_escape);

                let _ = handlebars
                    .register_template_string("template", viewer_template)
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap();
                handlebars.register_template_string("template", viewer_template)?;

                let mut data = BTreeMap::new();



@@ 151,15 130,9 @@ impl ImagApplication for ImagView {

                data.insert("entries", file_paths);

                let call = handlebars
                    .render("template", &data)
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap();
                let call = handlebars .render("template", &data)?;
                let mut elems = call.split_whitespace();
                let command_string = elems
                    .next()
                    .ok_or_else(|| Error::from(err_msg("No command")))
                    .map_err_trace_exit_unwrap();
                let command_string = elems.next().ok_or_else(|| err_msg("No command"))?;
                let mut cmd = Command::new(command_string);

                for arg in elems {


@@ 172,15 145,14 @@ impl ImagApplication for ImagView {
            debug!("Calling: {:?}", command);

            if !command
                .status()
                .map_err(Error::from)
                .map_err_trace_exit_unwrap()
                .status()?
                .success()
            {
                exit(1)
                return Err(err_msg("Failed to execute command"))
            }

            drop(files);
            Ok(())
        } else {
            let out         = rt.stdout();
            let mut outlock = out.lock();


@@ 202,32 174,30 @@ impl ImagApplication for ImagView {
                let viewer    = MarkdownViewer::new(&rt);
                let seperator = basesep.map(|s| build_seperator(s, sep_width));

                entries
                    .enumerate()
                    .for_each(|(n, entry)| {
                        if n != 0 {
                            seperator
                                .as_ref()
                                .map(|s| writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit());
                        }
                let mut i = 0; // poor mans enumerate()

                        if let Err(e) = viewer.view_entry(&entry, &mut outlock) {
                            handle_error(e);
                entries.and_then_ok(|entry| {
                    if i != 0 {
                        if let Some(s) = seperator.as_ref() {
                            writeln!(outlock, "{}", s)?;
                        }
                    }

                    viewer.view_entry(&entry, &mut outlock)?;

                        rt.report_touched(entry.get_location()).unwrap_or_exit();
                    });
                    i += 1;
                    rt.report_touched(entry.get_location()).map_err(Error::from)
                })
                .collect()
            } else {
                let mut viewer = StdoutViewer::new(view_header, !hide_content);

                if rt.cli().occurrences_of("autowrap") != 0 {
                    let width = rt.cli().value_of("autowrap").unwrap(); // ensured by clap
                    let width = usize::from_str(width).unwrap_or_else(|e| {
                        error!("Failed to parse argument to number: autowrap = {:?}",
                               rt.cli().value_of("autowrap").map(String::from));
                        error!("-> {:?}", e);
                        ::std::process::exit(1)
                    });
                    let width = usize::from_str(width).map_err(|_| {
                        format_err!("Failed to parse argument to number: autowrap = {:?}",
                               rt.cli().value_of("autowrap").map(String::from))
                    })?;

                    // Copying this value over, so that the seperator has the right len as well
                    sep_width = width;


@@ 236,25 206,22 @@ impl ImagApplication for ImagView {
                }

                let seperator = basesep.map(|s| build_seperator(s, sep_width));
                entries
                    .enumerate()
                    .for_each(|(n, entry)| {
                        if n != 0 {
                            seperator
                                .as_ref()
                                .map(|s| writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit());
                let mut i = 0; // poor mans enumerate()
                entries.and_then_ok(|entry| {
                    if i != 0 {
                        if let Some(s) = seperator.as_ref() {
                            writeln!(outlock, "{}", s)?;
                        }
                    }

                        if let Err(e) = viewer.view_entry(&entry, &mut outlock) {
                            handle_error(e);
                        }
                    viewer.view_entry(&entry, &mut outlock)?;

                        rt.report_touched(entry.get_location()).unwrap_or_exit();
                    });
                    i += 1;
                    rt.report_touched(entry.get_location()).map_err(Error::from)
                })
                .collect()
            }
        }

        Ok(())
    }

    fn build_cli<'a>(app: App<'a, 'a>) -> App<'a, 'a> {


@@ 275,41 242,25 @@ impl ImagApplication for ImagView {
}

fn create_tempfile_for<'a>(entry: &FileLockEntry<'a>, view_header: bool, hide_content: bool)
    -> (tempfile::NamedTempFile, String)
    -> Result<(tempfile::NamedTempFile, String)>
{
    let mut tmpfile = tempfile::NamedTempFile::new()
        .map_err(Error::from)
        .map_err_trace_exit_unwrap();
    let mut tmpfile = tempfile::NamedTempFile::new()?;

    if view_header {
        let hdr = toml::ser::to_string_pretty(entry.get_header())
            .map_err(Error::from)
            .map_err_trace_exit_unwrap();
        let _ = tmpfile.write(format!("---\n{}---\n", hdr).as_bytes())
            .map_err(Error::from)
            .map_err_trace_exit_unwrap();
        let hdr = toml::ser::to_string_pretty(entry.get_header())?;
        let _ = tmpfile.write(format!("---\n{}---\n", hdr).as_bytes())?;
    }

    if !hide_content {
        let _ = tmpfile.write(entry.get_content().as_bytes())
            .map_err(Error::from)
            .map_err_trace_exit_unwrap();
        let _ = tmpfile.write(entry.get_content().as_bytes())?;
    }

    let file_path = tmpfile
        .path()
        .to_str()
        .map(String::from)
        .ok_or_else(|| Error::from(err_msg("Cannot build path")))
        .map_err_trace_exit_unwrap();
        .ok_or_else(|| Error::from(err_msg("Cannot build path")))?;

    (tmpfile, file_path)
    Ok((tmpfile, file_path))
}

fn handle_error(e: ::libimagentryview::error::Error) {
    use libimagentryview::error::Error;
    match e {
        Error::Io(e)    => Err(e).to_exit_code().unwrap_or_exit(),
        Error::Other(e) => Err(e).map_err_trace_exit_unwrap()
    }
}

M bin/core/imag/Cargo.toml => bin/core/imag/Cargo.toml +1 -1
@@ 20,7 20,6 @@ libimagerror    = { version = "0.10.0", path = "../../../lib/core/libimagerror" 
libimagstore    = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
libimagentrytag = { version = "0.10.0", path = "../../../lib/entry/libimagentrytag" }
libimagutil     = { version = "0.10.0", path = "../../../lib/etc/libimagutil" }
failure	        = "0.1.5"
log             = "0.4.6"

# Build time dependencies for cli completion


@@ 61,6 60,7 @@ walkdir = "2.2.8"
log = "0.4.6"
toml = "0.5.1"
toml-query = "0.9.2"
failure    = "0.1.5"

libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" }

M bin/core/imag/src/main.rs => bin/core/imag/src/main.rs +149 -201
@@ 36,6 36,7 @@

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate failure;
extern crate walkdir;
extern crate toml;
extern crate toml_query;


@@ 44,11 45,10 @@ extern crate toml_query;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;
use std::io::{stdout, Stdout, Write};
use std::io::{stdout, Write};
use std::collections::BTreeMap;
use std::path::PathBuf;



@@ 56,12 56,13 @@ use walkdir::WalkDir;
use clap::{Arg, ArgMatches, AppSettings, SubCommand};
use toml::Value;
use toml_query::read::TomlValueReadExt;
use failure::Error;
use failure::ResultExt;
use failure::err_msg;
use failure::Fallible as Result;

use libimagrt::runtime::Runtime;
use libimagrt::spec::CliSpec;
use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagerror::trace::trace_error;
use libimagrt::configuration::InternalConfiguration;

/// Returns the helptext, putting the Strings in cmds as possible


@@ 107,54 108,44 @@ fn help_text(cmds: Vec<String>) -> String {
}

/// Returns the list of imag-* executables found in $PATH
fn get_commands(out: &mut Stdout) -> Vec<String> {
    let mut v = match env::var("PATH") {
        Err(e) => {
            writeln!(out, "PATH error: {:?}", e)
                .to_exit_code()
                .unwrap_or_exit();
            exit(1)
        },

        Ok(path) => path
            .split(':')
            .flat_map(|elem| {
                WalkDir::new(elem)
                    .max_depth(1)
                    .into_iter()
                    .filter(|path| match *path {
                        Ok(ref p) => p.file_name().to_str().map_or(false, |f| f.starts_with("imag-")),
                        Err(_)    => false,
                    })
                    .filter_map(Result::ok)
                    .filter_map(|path| path
                        .file_name()
                       .to_str()
                       .and_then(|s| s.splitn(2, '-').nth(1).map(String::from))
                    )
            })
            .filter(|path| if cfg!(debug_assertions) {
                // if we compile in debug mode during development, ignore everything that ends with
                // ".d", as developers might use the ./target/debug/ directory directly in `$PATH`.
                !path.ends_with(".d")
            } else {
                true
            })
            .collect::<Vec<String>>()
    };
fn get_commands() -> Result<Vec<String>> {
    let mut v = env::var("PATH")?
        .split(':')
        .flat_map(|elem| {
            WalkDir::new(elem)
                .max_depth(1)
                .into_iter()
                .filter(|path| match *path {
                    Ok(ref p) => p.file_name().to_str().map_or(false, |f| f.starts_with("imag-")),
                    Err(_)    => false,
                })
                .filter_map(|r| r.ok())
                .filter_map(|path| path
                    .file_name()
                   .to_str()
                   .and_then(|s| s.splitn(2, '-').nth(1).map(String::from))
                )
        })
        .filter(|path| if cfg!(debug_assertions) {
            // if we compile in debug mode during development, ignore everything that ends with
            // ".d", as developers might use the ./target/debug/ directory directly in `$PATH`.
            !path.ends_with(".d")
        } else {
            true
        })
        .collect::<Vec<String>>();

    v.sort();
    v
    Ok(v)
}


fn main() {
fn main() -> Result<()> {
    // Initialize the Runtime and build the CLI
    let appname  = "imag";
    let version  = make_imag_version!();
    let about    = "imag - the PIM suite for the commandline";
    let mut out  = stdout();
    let commands = get_commands(&mut out);
    let commands = get_commands()?;
    let helptext = help_text(commands.clone());
    let mut app  = Runtime::get_default_cli_builder(appname, &version, about)
        .settings(&[AppSettings::AllowExternalSubcommands, AppSettings::ArgRequiredElseHelp])


@@ 175,174 166,131 @@ fn main() {

    let long_help = {
        let mut v = vec![];
        if let Err(e) = app.write_long_help(&mut v) {
            eprintln!("Error: {:?}", e);
            exit(1);
        }
        String::from_utf8(v).unwrap_or_else(|_| { eprintln!("UTF8 Error"); exit(1) })
        app.write_long_help(&mut v)?;
        String::from_utf8(v).map_err(|_| err_msg("UTF8 Error"))?
    };
    {
        let print_help = app.clone().get_matches().subcommand_name().map(|h| h == "help").unwrap_or(false);
        if print_help {
            writeln!(out, "{}", long_help)
                .to_exit_code()
                .unwrap_or_exit();
            exit(0)
        }
    }

    let enable_logging = app.enable_logging();
    let matches = app.matches();

    let rtp = ::libimagrt::runtime::get_rtp_match(&matches)
        .unwrap_or_else(|e| {
            trace_error(&e);
            exit(1)
        });
    let configpath = matches
        .value_of("config")
        .map_or_else(|| rtp.clone(), PathBuf::from);
    debug!("Config path = {:?}", configpath);
    let config = ::libimagrt::configuration::fetch_config(&configpath)
        .unwrap_or_else(|e| {
            trace_error(&e);
            exit(1)
        });

    if enable_logging {
        Runtime::init_logger(&matches, config.as_ref())
    }

    debug!("matches: {:?}", matches);

    // Begin checking for arguments

    if matches.is_present("version") {
        debug!("Showing version");
        writeln!(out, "imag {}", env!("CARGO_PKG_VERSION"))
            .to_exit_code()
            .unwrap_or_exit();
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        commands
            .iter()
            .map(|command| {
                match Command::new(format!("imag-{}", command))
                    .stdin(::std::process::Stdio::inherit())
                    .stdout(::std::process::Stdio::piped())
                    .stderr(::std::process::Stdio::inherit())
                    .arg("--version")
                    .output()
                    .map(|v| v.stdout)
                {
                    Ok(s) => match String::from_utf8(s) {
                        Ok(s) => format!("{:15} -> {}", command, s),
                        Err(e) => format!("UTF8 Error while working with output of imag{}: {:?}", command, e),
                    },
                    Err(e) => format!("Failed calling imag-{} -> {:?}", command, e),
                }
            })
            .fold((), |_, line| {
                // The amount of newlines may differ depending on the subprocess
                writeln!(out, "{}", line.trim())
                    .to_exit_code()
                    .unwrap_or_exit();
            });

        exit(0);
    }
    let print_help = app.clone().get_matches().subcommand_name().map(|h| h == "help").unwrap_or(false);

    let aliases = match fetch_aliases(config.as_ref()) {
        Ok(aliases) => aliases,
        Err(e)      => {
            writeln!(out, "Error while fetching aliases from configuration file")
                .to_exit_code()
                .unwrap_or_exit();
            debug!("Error = {:?}", e);
            writeln!(out, "Aborting")
                .to_exit_code()
                .unwrap_or_exit();
            exit(1);
    let mut out  = stdout();
    if print_help {
        writeln!(out, "{}", long_help).map_err(Error::from)
    } else {
        let enable_logging = app.enable_logging();
        let matches = app.matches();

        let rtp = ::libimagrt::runtime::get_rtp_match(&matches)?;
        let configpath = matches
            .value_of("config")
            .map_or_else(|| rtp.clone(), PathBuf::from);
        debug!("Config path = {:?}", configpath);
        let config = ::libimagrt::configuration::fetch_config(&configpath)?;

        if enable_logging {
            Runtime::init_logger(&matches, config.as_ref())
        }
    };

    // Matches any subcommand given, except calling for example 'imag --versions', as this option
    // does not exit. There's nothing to do in such a case
    if let (subcommand, Some(scmd)) = matches.subcommand() {
        // Get all given arguments and further subcommands to pass to
        // the imag-<> binary
        // Providing no arguments is OK, and is therefore ignored here
        let mut subcommand_args : Vec<String> = match scmd.values_of("") {
            Some(values) => values.map(String::from).collect(),
            None => Vec::new()
        };

        debug!("Processing forwarding of commandline arguments");
        forward_commandline_arguments(&matches, &mut subcommand_args);

        let subcommand = String::from(subcommand);
        let subcommand = aliases.get(&subcommand).cloned().unwrap_or(subcommand);

        debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

        // Create a Command, and pass it the gathered arguments
        match Command::new(format!("imag-{}", subcommand))
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .args(&subcommand_args[..])
            .spawn()
            .and_then(|mut c| c.wait())
        {
            Ok(exit_status) => {
                if !exit_status.success() {
                    debug!("imag-{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                    eprintln!("imag-{} exited with non-zero exit code", subcommand);
                    exit(exit_status.code().unwrap_or(1));
                }
                debug!("Successful exit!");
            },

            Err(e) => {
                debug!("Error calling the subcommand");
                match e.kind() {
                    ErrorKind::NotFound => {
                        writeln!(out, "No such command: 'imag-{}'", subcommand)
                            .to_exit_code()
                            .unwrap_or_exit();
                        writeln!(out, "See 'imag --help' for available subcommands")
                            .to_exit_code()
                            .unwrap_or_exit();
                        exit(1);
                    },
                    ErrorKind::PermissionDenied => {
                        writeln!(out, "No permission to execute: 'imag-{}'", subcommand)
                            .to_exit_code()
                            .unwrap_or_exit();
                        exit(1);
                    },
                    _ => {
                        writeln!(out, "Error spawning: {:?}", e)
                            .to_exit_code()
                            .unwrap_or_exit();
                        exit(1);
        debug!("matches: {:?}", matches);

        // Begin checking for arguments

        if matches.is_present("version") {
            debug!("Showing version");
            writeln!(out, "imag {}", env!("CARGO_PKG_VERSION")).map_err(Error::from)
        } else {
            if matches.is_present("versions") {
                debug!("Showing versions");
                commands
                    .iter()
                    .map(|command| {
                        match Command::new(format!("imag-{}", command))
                            .stdin(::std::process::Stdio::inherit())
                            .stdout(::std::process::Stdio::piped())
                            .stderr(::std::process::Stdio::inherit())
                            .arg("--version")
                            .output()
                            .map(|v| v.stdout)
                        {
                            Ok(s) => match String::from_utf8(s) {
                                Ok(s) => format!("{:15} -> {}", command, s),
                                Err(e) => format!("UTF8 Error while working with output of imag{}: {:?}", command, e),
                            },
                            Err(e) => format!("Failed calling imag-{} -> {:?}", command, e),
                        }
                    })
                    .fold(Ok(()), |_, line| {
                        // The amount of newlines may differ depending on the subprocess
                        writeln!(out, "{}", line.trim()).map_err(Error::from)
                    })
            } else {
                let aliases = fetch_aliases(config.as_ref())
                    .map_err(Error::from)
                    .context("Error while fetching aliases from configuration file")?;

                // Matches any subcommand given, except calling for example 'imag --versions', as this option
                // does not exit. There's nothing to do in such a case
                if let (subcommand, Some(scmd)) = matches.subcommand() {
                    // Get all given arguments and further subcommands to pass to
                    // the imag-<> binary
                    // Providing no arguments is OK, and is therefore ignored here
                    let mut subcommand_args : Vec<String> = match scmd.values_of("") {
                        Some(values) => values.map(String::from).collect(),
                        None => Vec::new()
                    };

                    debug!("Processing forwarding of commandline arguments");
                    forward_commandline_arguments(&matches, &mut subcommand_args);

                    let subcommand = String::from(subcommand);
                    let subcommand = aliases.get(&subcommand).cloned().unwrap_or(subcommand);

                    debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

                    // Create a Command, and pass it the gathered arguments
                    match Command::new(format!("imag-{}", subcommand))
                        .stdin(Stdio::inherit())
                        .stdout(Stdio::inherit())
                        .stderr(Stdio::inherit())
                        .args(&subcommand_args[..])
                        .spawn()
                        .and_then(|mut c| c.wait())
                    {
                        Ok(exit_status) => if !exit_status.success() {
                            debug!("imag-{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                            Err(format_err!("imag-{} exited with non-zero exit code", subcommand))
                        } else {
                            debug!("Successful exit!");
                            Ok(())
                        },

                        Err(e) => {
                            debug!("Error calling the subcommand");
                            match e.kind() {
                                ErrorKind::NotFound => {
                                    writeln!(out, "No such command: 'imag-{}'", subcommand)?;