~matthiasbeyer/imag

a749d97a16af553e4a6f949538433067a757d9e8 — Matthias Beyer 3 years ago 281d4b4 + aff5059
Merge branch 'failure'

I'm so happy to finally be able to merge this patchset. After four days
of work, we finally convert the whole codebase from error_chain error
handling to failure.

Dependencies are now imported from "master" or even "failure" branches,
which will result in dependencies breaking the imag build as soon as the
"failure" branches vanish or the master breaks on the dependencies, but
we do it anyways until we are in release-shape.

Thanks goes to Kai for emotional support during the last weekend while
developing this patchset.

Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
231 files changed, 1582 insertions(+), 3407 deletions(-)

M bin/core/imag-annotate/Cargo.toml
M bin/core/imag-annotate/src/main.rs
M bin/core/imag-category/Cargo.toml
M bin/core/imag-diagnostics/Cargo.toml
M bin/core/imag-diagnostics/src/main.rs
M bin/core/imag-gps/Cargo.toml
M bin/core/imag-gps/src/main.rs
M bin/core/imag-ids/Cargo.toml
M bin/core/imag-ids/src/id_filters.rs
M bin/core/imag-ids/src/main.rs
M bin/core/imag-link/Cargo.toml
M bin/core/imag-link/src/main.rs
M bin/core/imag-mv/src/main.rs
M bin/core/imag-store/Cargo.toml
M bin/core/imag-store/src/create.rs
D bin/core/imag-store/src/error.rs
M bin/core/imag-store/src/main.rs
M bin/core/imag-tag/Cargo.toml
M bin/core/imag-tag/src/main.rs
M bin/core/imag-view/Cargo.toml
M bin/core/imag-view/src/main.rs
M bin/core/imag/Cargo.toml
M bin/core/imag/src/main.rs
M bin/domain/imag-bookmark/Cargo.toml
M bin/domain/imag-bookmark/src/main.rs
M bin/domain/imag-contact/Cargo.toml
M bin/domain/imag-contact/src/create.rs
M bin/domain/imag-contact/src/main.rs
M bin/domain/imag-diary/Cargo.toml
M bin/domain/imag-diary/src/create.rs
M bin/domain/imag-diary/src/list.rs
M bin/domain/imag-diary/src/main.rs
M bin/domain/imag-diary/src/util.rs
M bin/domain/imag-habit/Cargo.toml
M bin/domain/imag-habit/src/main.rs
M bin/domain/imag-log/Cargo.toml
M bin/domain/imag-log/src/main.rs
M bin/domain/imag-mail/Cargo.toml
M bin/domain/imag-mail/src/main.rs
M bin/domain/imag-timetrack/Cargo.toml
M bin/domain/imag-timetrack/src/day.rs
M bin/domain/imag-timetrack/src/list.rs
M bin/domain/imag-timetrack/src/main.rs
M bin/domain/imag-timetrack/src/month.rs
M bin/domain/imag-timetrack/src/start.rs
M bin/domain/imag-timetrack/src/stop.rs
M bin/domain/imag-timetrack/src/track.rs
M bin/domain/imag-timetrack/src/week.rs
M bin/domain/imag-timetrack/src/year.rs
M bin/domain/imag-todo/Cargo.toml
M bin/domain/imag-todo/src/main.rs
M bin/domain/imag-wiki/src/main.rs
M lib/core/libimagerror/Cargo.toml
A lib/core/libimagerror/src/errors.rs
M lib/core/libimagerror/src/iter.rs
M lib/core/libimagerror/src/lib.rs
M lib/core/libimagerror/src/trace.rs
M lib/core/libimagrt/Cargo.toml
M lib/core/libimagrt/src/configuration.rs
D lib/core/libimagrt/src/error.rs
M lib/core/libimagrt/src/lib.rs
M lib/core/libimagrt/src/logger.rs
M lib/core/libimagrt/src/runtime.rs
M lib/core/libimagstore/Cargo.toml
M lib/core/libimagstore/src/configuration.rs
D lib/core/libimagstore/src/error.rs
M lib/core/libimagstore/src/file_abstraction/fs.rs
M lib/core/libimagstore/src/file_abstraction/inmemory.rs
M lib/core/libimagstore/src/file_abstraction/iter.rs
M lib/core/libimagstore/src/file_abstraction/mod.rs
M lib/core/libimagstore/src/iter.rs
M lib/core/libimagstore/src/lib.rs
M lib/core/libimagstore/src/store.rs
M lib/core/libimagstore/src/storeid.rs
M lib/core/libimagstore/src/util.rs
M lib/domain/libimagbookmark/Cargo.toml
M lib/domain/libimagbookmark/src/collection.rs
D lib/domain/libimagbookmark/src/error.rs
M lib/domain/libimagbookmark/src/lib.rs
M lib/domain/libimagbookmark/src/link.rs
M lib/domain/libimagcontact/Cargo.toml
M lib/domain/libimagcontact/src/contact.rs
D lib/domain/libimagcontact/src/error.rs
M lib/domain/libimagcontact/src/iter.rs
M lib/domain/libimagcontact/src/lib.rs
M lib/domain/libimagcontact/src/store.rs
M lib/domain/libimagcontact/src/util.rs
M lib/domain/libimagdiary/Cargo.toml
M lib/domain/libimagdiary/src/diary.rs
M lib/domain/libimagdiary/src/diaryid.rs
M lib/domain/libimagdiary/src/entry.rs
D lib/domain/libimagdiary/src/error.rs
M lib/domain/libimagdiary/src/iter.rs
M lib/domain/libimagdiary/src/lib.rs
M lib/domain/libimagdiary/src/viewer.rs
M lib/domain/libimaghabit/Cargo.toml
D lib/domain/libimaghabit/src/error.rs
M lib/domain/libimaghabit/src/habit.rs
M lib/domain/libimaghabit/src/instance.rs
M lib/domain/libimaghabit/src/iter.rs
M lib/domain/libimaghabit/src/lib.rs
D lib/domain/libimaghabit/src/result.rs
M lib/domain/libimaghabit/src/store.rs
M lib/domain/libimaghabit/src/util.rs
M lib/domain/libimaglog/Cargo.toml
D lib/domain/libimaglog/src/error.rs
M lib/domain/libimaglog/src/lib.rs
M lib/domain/libimaglog/src/log.rs
M lib/domain/libimagmail/Cargo.toml
D lib/domain/libimagmail/src/error.rs
M lib/domain/libimagmail/src/iter.rs
M lib/domain/libimagmail/src/lib.rs
M lib/domain/libimagmail/src/mail.rs
M lib/domain/libimagnotes/Cargo.toml
D lib/domain/libimagnotes/src/error.rs
M lib/domain/libimagnotes/src/iter.rs
M lib/domain/libimagnotes/src/lib.rs
M lib/domain/libimagnotes/src/note.rs
M lib/domain/libimagnotes/src/notestore.rs
M lib/domain/libimagtimetrack/Cargo.toml
D lib/domain/libimagtimetrack/src/error.rs
M lib/domain/libimagtimetrack/src/iter/create.rs
M lib/domain/libimagtimetrack/src/iter/get.rs
M lib/domain/libimagtimetrack/src/iter/setendtime.rs
M lib/domain/libimagtimetrack/src/iter/storeid.rs
M lib/domain/libimagtimetrack/src/iter/tag.rs
M lib/domain/libimagtimetrack/src/lib.rs
M lib/domain/libimagtimetrack/src/tag.rs
M lib/domain/libimagtimetrack/src/timetracking.rs
M lib/domain/libimagtimetrack/src/timetrackingstore.rs
M lib/domain/libimagtodo/Cargo.toml
D lib/domain/libimagtodo/src/error.rs
M lib/domain/libimagtodo/src/iter.rs
M lib/domain/libimagtodo/src/lib.rs
M lib/domain/libimagtodo/src/task.rs
M lib/domain/libimagtodo/src/taskstore.rs
M lib/domain/libimagwiki/Cargo.toml
M lib/domain/libimagwiki/src/entry.rs
D lib/domain/libimagwiki/src/error.rs
M lib/domain/libimagwiki/src/lib.rs
M lib/domain/libimagwiki/src/store.rs
M lib/domain/libimagwiki/src/wiki.rs
M lib/entry/libimagentryannotation/Cargo.toml
M lib/entry/libimagentryannotation/src/annotateable.rs
M lib/entry/libimagentryannotation/src/annotation_fetcher.rs
D lib/entry/libimagentryannotation/src/error.rs
M lib/entry/libimagentryannotation/src/iter.rs
M lib/entry/libimagentryannotation/src/lib.rs
M lib/entry/libimagentrycategory/Cargo.toml
M lib/entry/libimagentrycategory/src/category.rs
M lib/entry/libimagentrycategory/src/entry.rs
D lib/entry/libimagentrycategory/src/error.rs
M lib/entry/libimagentrycategory/src/iter.rs
M lib/entry/libimagentrycategory/src/lib.rs
M lib/entry/libimagentrycategory/src/store.rs
M lib/entry/libimagentrydatetime/Cargo.toml
M lib/entry/libimagentrydatetime/src/datepath/compiler.rs
D lib/entry/libimagentrydatetime/src/datepath/error.rs
M lib/entry/libimagentrydatetime/src/datepath/mod.rs
M lib/entry/libimagentrydatetime/src/datepath/to_store_id.rs
M lib/entry/libimagentrydatetime/src/datetime.rs
D lib/entry/libimagentrydatetime/src/error.rs
M lib/entry/libimagentrydatetime/src/lib.rs
M lib/entry/libimagentrydatetime/src/range.rs
M lib/entry/libimagentryedit/Cargo.toml
M lib/entry/libimagentryedit/src/edit.rs
D lib/entry/libimagentryedit/src/error.rs
M lib/entry/libimagentryedit/src/lib.rs
M lib/entry/libimagentryfilter/Cargo.toml
M lib/entry/libimagentryfilter/src/builtin/header/field_eq.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_exists.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_grep.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_gt.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_isempty.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_istype.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_lt.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_predicate.rs
D lib/entry/libimagentryfilter/src/error.rs
M lib/entry/libimagentryfilter/src/lib.rs
M lib/entry/libimagentrygps/Cargo.toml
M lib/entry/libimagentrygps/src/entry.rs
D lib/entry/libimagentrygps/src/error.rs
M lib/entry/libimagentrygps/src/lib.rs
M lib/entry/libimagentrygps/src/types.rs
M lib/entry/libimagentrylink/Cargo.toml
D lib/entry/libimagentrylink/src/error.rs
M lib/entry/libimagentrylink/src/external.rs
M lib/entry/libimagentrylink/src/internal.rs
M lib/entry/libimagentrylink/src/lib.rs
M lib/entry/libimagentrymarkdown/Cargo.toml
D lib/entry/libimagentrymarkdown/src/error.rs
M lib/entry/libimagentrymarkdown/src/html.rs
M lib/entry/libimagentrymarkdown/src/lib.rs
M lib/entry/libimagentrymarkdown/src/link.rs
M lib/entry/libimagentrymarkdown/src/processor.rs
M lib/entry/libimagentryref/Cargo.toml
D lib/entry/libimagentryref/src/error.rs
M lib/entry/libimagentryref/src/generators/base.rs
M lib/entry/libimagentryref/src/generators/mod.rs
M lib/entry/libimagentryref/src/lib.rs
M lib/entry/libimagentryref/src/reference.rs
M lib/entry/libimagentryref/src/refstore.rs
M lib/entry/libimagentrytag/Cargo.toml
D lib/entry/libimagentrytag/src/error.rs
M lib/entry/libimagentrytag/src/lib.rs
M lib/entry/libimagentrytag/src/tag.rs
M lib/entry/libimagentrytag/src/tagable.rs
M lib/entry/libimagentryutil/Cargo.toml
D lib/entry/libimagentryutil/src/error.rs
M lib/entry/libimagentryutil/src/isa.rs
M lib/entry/libimagentryutil/src/lib.rs
M lib/entry/libimagentryview/Cargo.toml
M lib/entry/libimagentryview/src/builtin/editor.rs
M lib/entry/libimagentryview/src/builtin/md.rs
M lib/entry/libimagentryview/src/builtin/plain.rs
M lib/entry/libimagentryview/src/builtin/stdout.rs
D lib/entry/libimagentryview/src/error.rs
M lib/entry/libimagentryview/src/lib.rs
M lib/entry/libimagentryview/src/viewer.rs
M lib/etc/libimaginteraction/Cargo.toml
M lib/etc/libimaginteraction/src/ask.rs
D lib/etc/libimaginteraction/src/error.rs
M lib/etc/libimaginteraction/src/lib.rs
M lib/etc/libimaginteraction/src/readline.rs
M lib/etc/libimaginteraction/src/ui.rs
M lib/etc/libimagnotification/Cargo.toml
D lib/etc/libimagnotification/src/error.rs
M lib/etc/libimagnotification/src/lib.rs
M lib/etc/libimagnotification/src/notificator.rs
M lib/etc/libimagnotification/src/result_notification.rs
M lib/etc/libimagutil/src/testing.rs
M bin/core/imag-annotate/Cargo.toml => bin/core/imag-annotate/Cargo.toml +1 -0
@@ 26,6 26,7 @@ log = "0.4.0"
url = "1.2"
toml = "0.4"
toml-query = "0.7"
failure = "0.1"

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

M bin/core/imag-annotate/src/main.rs => bin/core/imag-annotate/src/main.rs +7 -4
@@ 35,6 35,7 @@
extern crate clap;
#[macro_use]
extern crate log;
extern crate failure;

extern crate libimagentryannotation;
extern crate libimagentryedit;


@@ 46,9 47,11 @@ extern crate libimagutil;
use std::io::Write;
use std::path::PathBuf;

use failure::Error;
use failure::err_msg;

use libimagentryannotation::annotateable::*;
use libimagentryannotation::annotation_fetcher::*;
use libimagentryannotation::error::AnnotationError as AE;
use libimagentryedit::edit::*;
use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;


@@ 97,7 100,7 @@ fn add(rt: &Runtime) {
    let _ = rt.store()
        .get(entry_name)
        .map_err_trace_exit_unwrap(1)
        .ok_or(AE::from("Entry does not exist".to_owned()))
        .ok_or_else(|| Error::from(err_msg("Entry does not exist".to_owned())))
        .map_err_trace_exit_unwrap(1)
        .annotate(rt.store(), annotation_name)
        .map_err_trace_exit_unwrap(1)


@@ 114,7 117,7 @@ fn remove(rt: &Runtime) {
    let mut entry = rt.store()
        .get(PathBuf::from(entry_name).into_storeid().map_err_trace_exit_unwrap(1))
        .map_err_trace_exit_unwrap(1)
        .ok_or(AE::from("Entry does not exist".to_owned()))
        .ok_or_else(|| Error::from(err_msg("Entry does not exist".to_owned())))
        .map_err_trace_exit_unwrap(1);

    let annotation = entry


@@ 148,7 151,7 @@ fn list(rt: &Runtime) {
                .store()
                .get(pb.into_storeid().map_err_trace_exit_unwrap(1))
                .map_err_trace_exit_unwrap(1)
                .ok_or(AE::from("Entry does not exist".to_owned()))
                .ok_or_else(|| Error::from(err_msg("Entry does not exist")))
                .map_err_trace_exit_unwrap(1)
                .annotations(rt.store())
                .map_err_trace_exit_unwrap(1)

M bin/core/imag-category/Cargo.toml => bin/core/imag-category/Cargo.toml +1 -1
@@ 24,7 24,7 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }

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

M bin/core/imag-diagnostics/Cargo.toml => bin/core/imag-diagnostics/Cargo.toml +2 -1
@@ 18,8 18,9 @@ build = "../../../build.rs"
[dependencies]
log  = "0.4"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
indicatif  = "0.9"
failure    = "0.1"

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

M bin/core/imag-diagnostics/src/main.rs => bin/core/imag-diagnostics/src/main.rs +8 -4
@@ 36,6 36,7 @@ extern crate clap;
extern crate toml;
extern crate toml_query;
extern crate indicatif;
extern crate failure;
#[macro_use] extern crate log;

#[macro_use] extern crate libimagrt;


@@ 52,12 53,14 @@ use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::error::StoreError as Error;
use libimagentrylink::internal::*;

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 std::collections::BTreeMap;



@@ 76,7 79,7 @@ struct Diagnostic {

impl Diagnostic {

    fn for_entry<'a>(entry: &FileLockEntry<'a>) -> Result<Diagnostic, ::libimagstore::error::StoreError> {
    fn for_entry<'a>(entry: &FileLockEntry<'a>) -> Result<Diagnostic> {
        Ok(Diagnostic {
            id: entry.get_location().clone(),
            entry_store_version: entry


@@ 142,7 145,7 @@ fn main() {
        .into_get_iter()
        .map(|e| {
            e.map_err_trace_exit_unwrap(1)
                .ok_or(Error::from("Unable to get entry".to_owned()))
                .ok_or_else(|| Error::from(err_msg("Unable to get entry".to_owned())))
                .map_err_trace_exit_unwrap(1)
        })
        .map(|e| {


@@ 163,7 166,7 @@ fn main() {

            diag
        })
        .collect::<Result<Vec<_>, _>>()
        .collect::<Result<Vec<_>>>()
        .map_err_trace_exit_unwrap(1);

    spinner.finish();


@@ 265,6 268,7 @@ fn main() {
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(1)
            .map(|opt| match opt {
                &Value::String(ref s) => s.to_owned(),

M bin/core/imag-gps/Cargo.toml => bin/core/imag-gps/Cargo.toml +1 -0
@@ 26,6 26,7 @@ log = "0.4.0"
url = "1.2"
toml = "0.4"
toml-query = "0.7"
failure = "0.1"

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

M bin/core/imag-gps/src/main.rs => bin/core/imag-gps/src/main.rs +5 -3
@@ 35,6 35,7 @@
extern crate clap;
#[macro_use]
extern crate log;
extern crate failure;

extern crate libimagentrygps;
#[macro_use] extern crate libimagrt;


@@ 47,8 48,9 @@ use std::process::exit;
use std::path::PathBuf;
use std::str::FromStr;

use libimagentrygps::error::GPSError as GE;
use libimagentrygps::error::GPSErrorKind as GEK;
use failure::Error;
use failure::err_msg;

use libimagentrygps::types::*;
use libimagentrygps::entry::*;
use libimagrt::setup::generate_runtime_setup;


@@ 100,7 102,7 @@ fn add(rt: &Runtime) {
                .map(|v| {debug!("Parsing = {}", v); v})
                .map(FromStr::from_str)
                .map(|elem| {
                    elem.or_else(|_| Err(GE::from(GEK::NumberConversionError)))
                    elem.or_else(|_| Err(Error::from(err_msg("Error while converting number"))))
                        .map_err_trace_exit_unwrap(1)
                })
                .collect::<Vec<i64>>();

M bin/core/imag-ids/Cargo.toml => bin/core/imag-ids/Cargo.toml +2 -1
@@ 26,8 26,9 @@ filters    = "0.3"
nom        = "3.2"
log        = "0.4"
toml       = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
is-match   = "0.1"
failure    = "0.1"

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

M bin/core/imag-ids/src/id_filters.rs => bin/core/imag-ids/src/id_filters.rs +2 -0
@@ 51,6 51,7 @@ pub mod header_filter_lang {

    use nom::digit;
    use nom::multispace;
    use failure::Error;

    use libimagstore::store::Entry;
    use libimagerror::trace::MapErrTrace;


@@ 403,6 404,7 @@ pub mod header_filter_lang {
            entry
                .get_header()
                .read(selector_str)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1)
                .map(|value| {
                    let comp = Comparator(&self.compare_operator, &self.compare_value);

M bin/core/imag-ids/src/main.rs => bin/core/imag-ids/src/main.rs +2 -1
@@ 39,6 39,7 @@ extern crate filters;
#[macro_use] extern crate is_match;
extern crate toml;
extern crate toml_query;
extern crate failure;

#[cfg(test)]
extern crate env_logger;


@@ 68,7 69,7 @@ fn main() {
    let version = make_imag_version!();
    let rt = generate_runtime_setup("imag-ids",
                                    &version,
                                    "Print all ids, optionally filtered with a user-defined filter",
                                    "print all ids",
                                    build_ui);

    let print_storepath = rt.cli().is_present("print-storepath");

M bin/core/imag-link/Cargo.toml => bin/core/imag-link/Cargo.toml +2 -1
@@ 25,8 25,9 @@ maintenance                       = { status     = "actively-developed" }
log = "0.4.0"
url = "1.5"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
prettytable-rs = "0.8"
failure        = "0.1"

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

M bin/core/imag-link/src/main.rs => bin/core/imag-link/src/main.rs +22 -19
@@ 35,6 35,7 @@
#[macro_use] extern crate log;
extern crate clap;
extern crate url;
extern crate failure;
#[macro_use] extern crate prettytable;
#[cfg(test)] extern crate toml;
#[cfg(test)] extern crate toml_query;


@@ 55,22 56,24 @@ extern crate libimagutil;
use std::io::Write;
use std::path::PathBuf;

use failure::Error;
use failure::err_msg;

use libimagentrylink::external::ExternalLinker;
use libimagentrylink::internal::InternalLinker;
use libimagentrylink::internal::store_check::StoreLinkConsistentExt;
use libimagentrylink::error::LinkError as LE;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::error::StoreError;
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;

mod ui;



@@ 80,7 83,7 @@ fn main() {
    let version = make_imag_version!();
    let rt = generate_runtime_setup("imag-link",
                                    &version,
                                    "Add/Remove links between entries",
                                    "Link entries",
                                    build_ui);
    if rt.cli().is_present("check-consistency") {
        let exit_code = match rt.store().check_link_consistency() {


@@ 119,11 122,11 @@ fn main() {
                warn_exit("No commandline call", 1)
            }
        })
        .ok_or(LE::from("No commandline call".to_owned()))
        .ok_or_else(|| Error::from(err_msg("No commandline call".to_owned())))
        .map_err_trace_exit_unwrap(1);
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>> {
    use libimagstore::storeid::StoreId;

    debug!("Getting: {:?}", name);


@@ 336,11 339,12 @@ mod tests {

    use toml::value::Value;
    use toml_query::read::TomlValueReadExt;
    use toml_query::error::Result as TomlQueryResult;
    use failure::Fallible as Result;
    use failure::Error;

    use libimagrt::runtime::Runtime;
    use libimagstore::storeid::StoreId;
    use libimagstore::store::{Result as StoreResult, FileLockEntry, Entry};
    use libimagstore::store::{FileLockEntry, Entry};

    fn setup_logging() {
        let _ = ::env_logger::try_init();


@@ 355,7 359,7 @@ mod tests {
    use self::mock::generate_test_runtime;
    use self::mock::reset_test_runtime;

    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> Result<StoreId> {
        let mut path = PathBuf::new();
        path.set_file_name(name);



@@ 376,11 380,10 @@ mod tests {
        Ok(id)
    }

    fn get_entry_links<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult<&'a Value> {
        match entry.get_header().read(&"links.internal".to_owned()) {
            Err(e) => Err(e),
            Ok(Some(v)) => Ok(v),
            Ok(None) => panic!("Didn't find 'links' in {:?}", entry),
    fn get_entry_links<'a>(entry: &'a FileLockEntry<'a>) -> Result<&'a Value> {
        match entry.get_header().read(&"links.internal".to_owned()).map_err(Error::from)? {
            Some(v) => Ok(v),
            None    => panic!("Didn't find 'links' in {:?}", entry),
        }
    }



@@ 394,7 397,7 @@ mod tests {
    #[test]
    fn test_link_modificates() {
        setup_logging();
        let rt = generate_test_runtime(vec!["test1", "test2"])
        let rt = generate_test_runtime(vec!["internal", "test1", "test2"])
            .unwrap();

        debug!("Runtime created");


@@ 423,7 426,7 @@ mod tests {
    #[test]
    fn test_linking_links() {
        setup_logging();
        let rt = generate_test_runtime(vec!["test1", "test2"])
        let rt = generate_test_runtime(vec!["internal", "test1", "test2"])
            .unwrap();

        debug!("Runtime created");


@@ 452,7 455,7 @@ mod tests {
    #[test]
    fn test_multilinking() {
        setup_logging();
        let rt = generate_test_runtime(vec!["test1", "test2"])
        let rt = generate_test_runtime(vec!["internal", "test1", "test2"])
            .unwrap();

        debug!("Runtime created");


@@ 482,7 485,7 @@ mod tests {
    #[test]
    fn test_linking_more_than_two() {
        setup_logging();
        let rt = generate_test_runtime(vec!["test1", "test2", "test3"])
        let rt = generate_test_runtime(vec!["internal", "test1", "test2", "test3"])
            .unwrap();

        debug!("Runtime created");


@@ 519,7 522,7 @@ mod tests {
    #[test]
    fn test_linking_links_unlinking_removes_links() {
        setup_logging();
        let rt = generate_test_runtime(vec!["test1", "test2"])
        let rt = generate_test_runtime(vec!["internal", "test1", "test2"])
            .unwrap();

        debug!("Runtime created");


@@ 555,7 558,7 @@ mod tests {
    #[test]
    fn test_linking_and_unlinking_more_than_two() {
        setup_logging();
        let rt = generate_test_runtime(vec!["test1", "test2", "test3"])
        let rt = generate_test_runtime(vec!["internal", "test1", "test2", "test3"])
            .unwrap();

        debug!("Runtime created");

M bin/core/imag-mv/src/main.rs => bin/core/imag-mv/src/main.rs +1 -2
@@ 53,7 53,6 @@ use libimagerror::iter::TraceIterator;
use libimagstore::storeid::StoreId;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::error::StoreError;
use libimagentrylink::internal::InternalLinker;
use libimagstore::iter::get::StoreIdGetIteratorExtension;



@@ 93,7 92,7 @@ fn main() {
            })
            .get_internal_links()
            .map_err_trace_exit_unwrap(1)
            .map(|link| Ok(link.get_store_id().clone()) as Result<_, StoreError>)
            .map(|link| Ok(link.get_store_id().clone()) as Result<_, _>)
            .into_get_iter(rt.store())
            .trace_unwrap_exit(1)
            .map(|e| {

M bin/core/imag-store/Cargo.toml => bin/core/imag-store/Cargo.toml +1 -1
@@ 24,7 24,7 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
error-chain = "0.12"
failure = "0.1"

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

M bin/core/imag-store/src/create.rs => bin/core/imag-store/src/create.rs +4 -14
@@ 20,12 20,13 @@
use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::result::Result as RResult;
use std::io::Read;
use std::ops::DerefMut;

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

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;


@@ 33,13 34,8 @@ use libimagstore::storeid::StoreId;
use libimagerror::trace::MapErrTrace;
use libimagutil::debug_result::*;

use error::StoreError;
use error::StoreErrorKind;
use error::ResultExt;
use util::build_toml_header;

type Result<T> = RResult<T, StoreError>;

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


@@ 95,13 91,9 @@ fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> R
fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> Result<()> {
    let content = matches
        .value_of("from-raw")
        .ok_or(StoreError::from_kind(StoreErrorKind::NoCommandlineCall))
        .map(string_from_raw_src);
        .ok_or_else(|| err_msg("No Commandline call"))
        .map(string_from_raw_src)?;

    if content.is_err() {
        return content.map(|_| ());
    }
    let content = content.unwrap();
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])


@@ 118,7 110,6 @@ fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &StoreId) -> Res
            r
        })
        .map_dbg_err(|e| format!("Error storing entry: {:?}", e))
        .chain_err(|| StoreErrorKind::BackendError)
}

fn create_with_content_and_header(rt: &Runtime,


@@ 142,7 133,6 @@ fn create_with_content_and_header(rt: &Runtime,
                debug!("New header set");
            }
        })
        .chain_err(|| StoreErrorKind::BackendError)
}

fn string_from_raw_src(raw_src: &str) -> String {

D bin/core/imag-store/src/error.rs => bin/core/imag-store/src/error.rs +0 -38
@@ 1,38 0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

error_chain! {
    types {
        StoreError, StoreErrorKind, ResultExt, Result;
    }

    errors {
        BackendError      {
            description("Backend Error")
            display("Backend Error")
        }

        NoCommandlineCall {
            description("No commandline call")
            display("No commandline call")
        }

    }
}


M bin/core/imag-store/src/main.rs => bin/core/imag-store/src/main.rs +1 -2
@@ 36,7 36,7 @@ extern crate clap;
#[macro_use] extern crate log;
extern crate toml;
#[cfg(test)] extern crate toml_query;
#[macro_use] extern crate error_chain;
extern crate failure;

#[macro_use] extern crate libimagrt;
extern crate libimagstore;


@@ 54,7 54,6 @@ use libimagerror::trace::MapErrTrace;

mod create;
mod delete;
mod error;
mod get;
mod retrieve;
mod ui;

M bin/core/imag-tag/Cargo.toml => bin/core/imag-tag/Cargo.toml +2 -1
@@ 37,8 37,9 @@ default-features = false
features = ["color", "suggestions", "wrap_help"]

[dev-dependencies]
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
env_logger = "0.5"
failure = "0.1"

[dev-dependencies.libimagutil]
version          = "0.9.0"

M bin/core/imag-tag/src/main.rs => bin/core/imag-tag/src/main.rs +8 -6
@@ 36,6 36,7 @@ extern crate clap;
#[macro_use] extern crate log;

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

extern crate libimagstore;
#[macro_use] extern crate libimagrt;


@@ 80,7 81,7 @@ fn main() {
    let version = make_imag_version!();
    let rt = generate_runtime_setup("imag-tag",
                                    &version,
                                    "Add and remove tags for entries",
                                    "Direct interface to the store. Use with great care!",
                                    build_ui);

    let ids : Vec<PathBuf> = rt


@@ 269,11 270,12 @@ mod tests {

    use toml::value::Value;
    use toml_query::read::TomlValueReadExt;
    use toml_query::error::Result as TomlQueryResult;
    use failure::Fallible as Result;
    use failure::Error;

    use libimagrt::runtime::Runtime;
    use libimagstore::storeid::StoreId;
    use libimagstore::store::{Result as StoreResult, FileLockEntry, Entry};
    use libimagstore::store::{FileLockEntry, Entry};

    use super::*;



@@ 285,7 287,7 @@ mod tests {
    }
    use self::mock::generate_test_runtime;

    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> Result<StoreId> {
        let mut path = PathBuf::new();
        path.set_file_name(name);



@@ 300,8 302,8 @@ mod tests {
        Ok(id)
    }

    fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult<Option<&'a Value>> {
        entry.get_header().read(&"tag.values".to_owned())
    fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> Result<Option<&'a Value>> {
        entry.get_header().read(&"tag.values".to_owned()).map_err(Error::from)
    }

    fn tags_toml_value<'a, I: IntoIterator<Item = &'static str>>(tags: I) -> Value {

M bin/core/imag-view/Cargo.toml => bin/core/imag-view/Cargo.toml +2 -1
@@ 24,9 24,10 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
handlebars = "1.0"
tempfile = "3"
failure = "0.1"

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

M bin/core/imag-view/src/main.rs => bin/core/imag-view/src/main.rs +17 -25
@@ 38,6 38,7 @@ extern crate handlebars;
extern crate tempfile;
extern crate toml;
extern crate toml_query;
extern crate failure;

extern crate libimagentryview;
extern crate libimagerror;


@@ 55,10 56,11 @@ use std::process::exit;

use handlebars::Handlebars;
use toml_query::read::TomlValueReadTypeExt;
use failure::Error;
use failure::err_msg;

use libimagrt::setup::generate_runtime_setup;
use libimagrt::runtime::Runtime;
use libimagerror::str::ErrFromStr;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::io::ToExitCode;


@@ 66,10 68,8 @@ use libimagerror::exit::ExitUnwrap;
use libimagentryview::builtin::stdout::StdoutViewer;
use libimagentryview::builtin::md::MarkdownViewer;
use libimagentryview::viewer::Viewer;
use libimagentryview::error::ViewError as VE;
use libimagstore::storeid::IntoStoreId;
use libimagstore::storeid::StoreIdIterator;
use libimagstore::error::StoreError;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagstore::store::FileLockEntry;



@@ 92,8 92,7 @@ fn main() {
            .into_get_iter(rt.store())
            .trace_unwrap_exit(1)
            .map(|e| {
                 e.ok_or_else(|| String::from("Entry not found"))
                     .map_err(StoreError::from)
                 e.ok_or_else(|| err_msg("Entry not found"))
                     .map_err_trace_exit_unwrap(1)
            })
            .map(|entry| create_tempfile_for(&entry, view_header, hide_content))


@@ 103,18 102,19 @@ fn main() {
            let viewer = rt
                .cli()
                .value_of("in")
                .ok_or_else::<VE, _>(|| "No viewer given".to_owned().into())
                .ok_or_else(|| Error::from(err_msg("No viewer given")))
                .map_err_trace_exit_unwrap(1);

            let config = rt
                .config()
                .ok_or_else::<VE, _>(|| "No configuration, cannot continue".to_owned().into())
                .ok_or_else(|| Error::from(err_msg("No configuration, cannot continue")))
                .map_err_trace_exit_unwrap(1);

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

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


@@ 126,8 126,7 @@ fn main() {

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

            let mut data = BTreeMap::new();


@@ 142,13 141,12 @@ fn main() {

            let call = handlebars
                .render("template", &data)
                .err_from_str()
                .map_err(VE::from)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1);
            let mut elems = call.split_whitespace();
            let command_string = elems
                .next()
                .ok_or::<VE>("No command".to_owned().into())
                .ok_or_else(|| Error::from(err_msg("No command")))
                .map_err_trace_exit_unwrap(1);
            let mut cmd = Command::new(command_string);



@@ 163,8 161,7 @@ fn main() {

        if !command
            .status()
            .err_from_str()
            .map_err(VE::from)
            .map_err(Error::from)
            .map_err_trace_exit_unwrap(1)
            .success()
        {


@@ 177,8 174,7 @@ fn main() {
            .into_get_iter(rt.store())
            .map(|e| {
                 e.map_err_trace_exit_unwrap(1)
                     .ok_or_else(|| String::from("Entry not found"))
                     .map_err(StoreError::from)
                     .ok_or_else(|| err_msg("Entry not found"))
                     .map_err_trace_exit_unwrap(1)
            });



@@ 282,25 278,21 @@ fn create_tempfile_for<'a>(entry: &FileLockEntry<'a>, view_header: bool, hide_co
    -> (tempfile::NamedTempFile, String)
{
    let mut tmpfile = tempfile::NamedTempFile::new()
        .err_from_str()
        .map_err(VE::from)
        .map_err(Error::from)
        .map_err_trace_exit_unwrap(1);

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

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



@@ 308,7 300,7 @@ fn create_tempfile_for<'a>(entry: &FileLockEntry<'a>, view_header: bool, hide_co
        .path()
        .to_str()
        .map(String::from)
        .ok_or::<VE>("Cannot build path".to_owned().into())
        .ok_or_else(|| Error::from(err_msg("Cannot build path")))
        .map_err_trace_exit_unwrap(1);

    (tmpfile, file_path)

M bin/core/imag/Cargo.toml => bin/core/imag/Cargo.toml +0 -1
@@ 32,7 32,6 @@ walkdir = "2"
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
is-match = "0.1"

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

M bin/core/imag/src/main.rs => bin/core/imag/src/main.rs +6 -14
@@ 37,7 37,6 @@ extern crate clap;
extern crate walkdir;
extern crate toml;
extern crate toml_query;
#[macro_use] extern crate is_match;

#[macro_use] extern crate libimagrt;
extern crate libimagerror;


@@ 56,7 55,6 @@ use clap::{Arg, ArgMatches, AppSettings, SubCommand};
use toml::Value;
use toml_query::read::TomlValueReadExt;

use libimagrt::error::RuntimeErrorKind;
use libimagrt::runtime::Runtime;
use libimagrt::spec::CliSpec;
use libimagerror::io::ToExitCode;


@@ 91,7 89,7 @@ fn help_text(cmds: Vec<String>) -> String {
    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://git.imag-pim.org/imag to view the source code,
    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1


@@ 190,17 188,11 @@ fn main() {
        .value_of(Runtime::arg_config_name())
        .map_or_else(|| rtp.clone(), PathBuf::from);
    debug!("Config path = {:?}", configpath);
    let config = match ::libimagrt::configuration::fetch_config(&configpath) {
            Ok(c) => Some(c),
            Err(e) => if !is_match!(e.kind(), &RuntimeErrorKind::ConfigNoConfigFileFound) {
                trace_error(&e);
                ::std::process::exit(1)
            } else {
                println!("No config file found.");
                println!("Continuing without configuration file");
                None
            },
    };
    let config = ::libimagrt::configuration::fetch_config(&configpath)
        .unwrap_or_else(|e| {
            trace_error(&e);
            exit(1)
        });

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


M bin/domain/imag-bookmark/Cargo.toml => bin/domain/imag-bookmark/Cargo.toml +2 -1
@@ 24,7 24,8 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
failure = "0.1"

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

M bin/domain/imag-bookmark/src/main.rs => bin/domain/imag-bookmark/src/main.rs +6 -4
@@ 36,6 36,7 @@ extern crate clap;
#[macro_use] extern crate log;
extern crate toml;
extern crate toml_query;
#[macro_use] extern crate failure;

extern crate libimagbookmark;
#[macro_use] extern crate libimagrt;


@@ 46,12 47,12 @@ use std::io::Write;
use std::process::exit;

use toml_query::read::TomlValueReadTypeExt;
use failure::Error;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagbookmark::collection::BookmarkCollection;
use libimagbookmark::collection::BookmarkCollectionStore;
use libimagbookmark::error::BookmarkError as BE;
use libimagbookmark::link::Link as BookmarkLink;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagerror::io::ToExitCode;


@@ 94,7 95,7 @@ fn add(rt: &Runtime) {

    let mut collection = BookmarkCollectionStore::get(rt.store(), &coll)
        .map_err_trace_exit_unwrap(1)
        .ok_or(BE::from(format!("No bookmark collection '{}' found", coll)))
        .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll))
        .map_err_trace_exit_unwrap(1);

    for url in scmd.values_of("urls").unwrap() { // unwrap saved by clap


@@ 135,7 136,7 @@ fn list(rt: &Runtime) {

    let collection = BookmarkCollectionStore::get(rt.store(), &coll)
        .map_err_trace_exit_unwrap(1)
        .ok_or(BE::from(format!("No bookmark collection '{}' found", coll)))
        .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll))
        .map_err_trace_exit_unwrap(1);

    let links   = collection.links(rt.store()).map_err_trace_exit_unwrap(1);


@@ 157,7 158,7 @@ fn remove(rt: &Runtime) {

    let mut collection = BookmarkCollectionStore::get(rt.store(), &coll)
        .map_err_trace_exit_unwrap(1)
        .ok_or(BE::from(format!("No bookmark collection '{}' found", coll)))
        .ok_or_else(|| format_err!("No bookmark collection '{}' found", coll))
        .map_err_trace_exit_unwrap(1);

    for url in scmd.values_of("urls").unwrap() { // enforced by clap


@@ 182,6 183,7 @@ fn get_collection_name(rt: &Runtime,
            rt.config()
                .map(|cfg| {
                    cfg.read_string("bookmark.default_collection")
                        .map_err(Error::from)
                        .map_err_trace_exit_unwrap(1)
                        .ok_or_else(|| {
                            error!("Missing config: 'bookmark.default_collection'. Set or use commandline to specify.");

M bin/domain/imag-contact/Cargo.toml => bin/domain/imag-contact/Cargo.toml +3 -2
@@ 24,12 24,13 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
vobject    = { git = "https://github.com/matthiasbeyer/rust-vobject", branch = "update-errorchain" }
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
vobject    = { git = "https://github.com/matthiasbeyer/rust-vobject", branch = "master" }
handlebars = "1.0"
walkdir = "2"
uuid = { version = "0.7", features = ["v4"] }
serde_json = "1"
failure = "0.1"

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

M bin/domain/imag-contact/src/create.rs => bin/domain/imag-contact/src/create.rs +10 -12
@@ 44,11 44,10 @@ use vobject::write_component;
use toml_query::read::TomlValueReadExt;
use toml::Value;
use uuid::Uuid;
use failure::Error;

use libimagcontact::store::ContactStore;
use libimagcontact::error::ContactError as CE;
use libimagrt::runtime::Runtime;
use libimagerror::str::ErrFromStr;
use libimagerror::trace::MapErrTrace;
use libimagerror::trace::trace_error;
use libimagutil::warn_result::WarnResult;


@@ 126,8 125,7 @@ pub fn create(rt: &Runtime) {
                .create_new(true)
                .open(fl.clone())
                .map_warn_err_str("Cannot create/open destination File. Stopping.")
                .err_from_str()
                .map_err(CE::from)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1);

            let uuid_string = uuid


@@ 161,8 159,7 @@ pub fn create(rt: &Runtime) {

        match ::toml::de::from_str(&template)
            .map(|toml| parse_toml_into_vcard(toml, uuid.clone()))
            .err_from_str()
            .map_err(CE::from)
            .map_err(Error::from)
        {
            Err(e) => {
                error!("Error parsing template");


@@ 183,7 180,7 @@ pub fn create(rt: &Runtime) {
                let vcard_string = write_component(&vcard);
                let _ = dest
                    .write_all(&vcard_string.as_bytes())
                    .map_err(CE::from)
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap(1);

                break;


@@ 244,7 241,7 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option<Vcard> {

    { // parse nicknames
        debug!("Parsing nicknames");
        match toml.read("nickname").map_err_trace_exit_unwrap(1) {
        match toml.read("nickname").map_err(Error::from).map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let nicktype = match read_str_from_toml(element, "type", false) {


@@ 306,7 303,7 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option<Vcard> {

    { // parse phone
        debug!("Parse phone");
        match toml.read("person.phone").map_err_trace_exit_unwrap(1) {
        match toml.read("person.phone").map_err(Error::from).map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let phonetype = match read_str_from_toml(element, "type", false) {


@@ 344,7 341,7 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option<Vcard> {

    { // parse address
        debug!("Parsing address");
        match toml.read("addresses").map_err_trace_exit_unwrap(1) {
        match toml.read("addresses").map_err(Error::from).map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let adrtype  = match read_str_from_toml(element, "type", false) {


@@ 391,7 388,7 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option<Vcard> {

    { // parse email
        debug!("Parsing email");
        match toml.read("person.email").map_err_trace_exit_unwrap(1) {
        match toml.read("person.email").map_err(Error::from).map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let mailtype  = match read_str_from_toml(element, "type", false) {


@@ 456,7 453,7 @@ fn parse_toml_into_vcard(toml: Value, uuid: String) -> Option<Vcard> {
}

fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>> {
    match toml.read(path).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
    match toml.read(path).map_err(Error::from).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
        Ok(Some(&Value::Array(ref vec))) => {
            let mut v = Vec::new();
            for elem in vec {


@@ 486,6 483,7 @@ fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>

fn read_str_from_toml(toml: &Value, path: &'static str, must_be_there: bool) -> Option<String> {
    let v = toml.read(path)
        .map_err(Error::from)
        .map_warn_err_str(&format!("Failed to read value at '{}'", path));

    match v {

M bin/domain/imag-contact/src/main.rs => bin/domain/imag-contact/src/main.rs +12 -15
@@ 41,6 41,7 @@ extern crate handlebars;
extern crate walkdir;
extern crate uuid;
extern crate serde_json;
extern crate failure;

extern crate libimagcontact;
extern crate libimagstore;


@@ 58,16 59,16 @@ use handlebars::Handlebars;
use clap::ArgMatches;
use toml_query::read::TomlValueReadTypeExt;
use walkdir::WalkDir;
use failure::Error;
use failure::err_msg;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagerror::str::ErrFromStr;
use libimagerror::trace::MapErrTrace;
use libimagerror::io::ToExitCode;
use libimagerror::exit::ExitUnwrap;
use libimagerror::iter::TraceIterator;
use libimagcontact::store::ContactStore;
use libimagcontact::error::ContactError as CE;
use libimagcontact::contact::Contact;
use libimagcontact::deser::DeserVcard;
use libimagstore::iter::get::StoreIdGetIteratorExtension;


@@ 121,7 122,7 @@ fn list(rt: &Runtime) {
        .map(|fle| {
             let fle = fle
                .map_err_trace_exit_unwrap(1)
                .ok_or_else(|| CE::from("StoreId not found".to_owned()))
                .ok_or_else(|| Error::from(err_msg("StoreId not found".to_owned())))
                .map_err_trace_exit_unwrap(1);

            fle.deser().map_err_trace_exit_unwrap(1)


@@ 144,8 145,7 @@ fn list(rt: &Runtime) {
                let data = build_data_object_for_handlebars(i, &deservcard);

                list_format.render("format", &data)
                    .err_from_str()
                    .map_err(CE::from)
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap(1)
            })



@@ 175,8 175,7 @@ fn import(rt: &Runtime) {
    } else if path.is_dir() {
        for entry in WalkDir::new(path).min_depth(1).into_iter() {
            let entry = entry
                .err_from_str()
                .map_err(CE::from)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1);
            if entry.file_type().is_file() {
                let pb = PathBuf::from(entry.path());


@@ 233,8 232,7 @@ fn show(rt: &Runtime) {

            let s = show_format
                .render("format", &data)
                .err_from_str()
                .map_err(CE::from)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1);
            let _ = writeln!(outlock, "{}", s).to_exit_code().unwrap_or_exit();
        });


@@ 324,8 322,7 @@ fn find(rt: &Runtime) {
            let data = build_data_object_for_handlebars(i, &card);
            let s = fmt
                .render("format", &data)
                .err_from_str()
                .map_err(CE::from)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1);

            let _ = writeln!(rt.stdout(), "{}", s)


@@ 341,19 338,19 @@ fn get_contact_print_format(config_value_path: &'static str, rt: &Runtime, scmd:
        .map(String::from)
        .unwrap_or_else(|| {
            rt.config()
                .ok_or_else(|| CE::from("No configuration file".to_owned()))
                .ok_or_else(|| Error::from(err_msg("No configuration file")))
                .map_err_trace_exit_unwrap(1)
                .read_string(config_value_path)
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1)
                .ok_or_else(|| CE::from("Configuration 'contact.list_format' does not exist".to_owned()))
                .ok_or_else(|| Error::from(err_msg("Configuration 'contact.list_format' does not exist")))
                .map_err_trace_exit_unwrap(1)
        });

    let mut hb = Handlebars::new();
    let _ = hb
        .register_template_string("format", fmt)
        .err_from_str()
        .map_err(CE::from)
        .map_err(Error::from)
        .map_err_trace_exit_unwrap(1);

    hb.register_escape_fn(::handlebars::no_escape);

M bin/domain/imag-diary/Cargo.toml => bin/domain/imag-diary/Cargo.toml +2 -1
@@ 25,8 25,9 @@ maintenance                       = { status     = "actively-developed" }
chrono = "0.4"
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
itertools = "0.7"
failure = "0.1"

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

M bin/domain/imag-diary/src/create.rs => bin/domain/imag-diary/src/create.rs +7 -5
@@ 21,10 21,11 @@ use clap::ArgMatches;
use chrono::NaiveDateTime;
use chrono::Local;
use chrono::Timelike;
use failure::Error;
use failure::ResultExt;
use failure::err_msg;

use libimagdiary::diary::Diary;
use libimagdiary::error::DiaryErrorKind as DEK;
use libimagdiary::error::ResultExt;
use libimagentryedit::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;


@@ 47,8 48,7 @@ pub fn create(rt: &Runtime) {
        Ok(())
    } else {
        debug!("Editing new diary entry");
        entry.edit_content(rt)
            .chain_err(|| DEK::DiaryEditError)
        entry.edit_content(rt).context(err_msg("Diary edit error")).map_err(Error::from)
    };

    let _ = res.map_err_trace_exit_unwrap(1);


@@ 75,7 75,9 @@ fn create_entry<'a>(diary: &'a Store, diaryname: &str, rt: &Runtime) -> FileLock
        })
        .map(|timed| {
            let time = create_id_from_clispec(&create, &diaryname, timed);
            diary.new_entry_at(&diaryname, &time).chain_err(|| DEK::StoreWriteError)
            diary.new_entry_at(&diaryname, &time)
                .context(err_msg("Store write error"))
                .map_err(Error::from)
        })
        .unwrap_or_else(|| {
            debug!("Creating non-timed entry");

M bin/domain/imag-diary/src/list.rs => bin/domain/imag-diary/src/list.rs +2 -1
@@ 29,9 29,10 @@ use libimagerror::exit::ExitUnwrap;
use libimagutil::debug_result::*;
use libimagdiary::diaryid::DiaryId;
use libimagdiary::diaryid::FromStoreId;
use libimagdiary::error::Result;
use libimagstore::storeid::IntoStoreId;

use failure::Fallible as Result;

use util::get_diary_name;

pub fn list(rt: &Runtime) {

M bin/domain/imag-diary/src/main.rs => bin/domain/imag-diary/src/main.rs +1 -0
@@ 33,6 33,7 @@
)]

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

M bin/domain/imag-diary/src/util.rs => bin/domain/imag-diary/src/util.rs +8 -4
@@ 18,10 18,13 @@
//

use libimagrt::runtime::Runtime;
use libimagdiary::error::*;
use libimagerror::errors::ErrorMsg as EM;

use toml::Value;
use toml_query::read::TomlValueReadExt;
use failure::Error;
use failure::Fallible as Result;
use failure::ResultExt;

pub fn get_diary_name(rt: &Runtime) -> Option<String> {
    use libimagdiary::config::get_default_diary_name;


@@ 58,14 61,15 @@ pub fn get_diary_timed_config(rt: &Runtime, diary_name: &str) -> Result<Option<T
        Some(cfg) => {
            let v = cfg
                .read(&format!("diary.diaries.{}.timed", diary_name))
                .chain_err(|| DiaryErrorKind::IOError);
                .context(EM::IO)
                .map_err(Error::from);

            match v {
                Ok(Some(&Value::String(ref s))) => parse_timed_string(s, diary_name).map(Some),

                Ok(Some(_)) => {
                    let s = format!("Type error at 'diary.diaryies.{}.timed': should be either 'd'/'daily', 'h'/'hourly', 'm'/'minutely' or 's'/'secondly'", diary_name);
                    Err(s).map_err(From::from)
                    Err(format_err!("{}", s))
                },

                Ok(None) => Ok(None),


@@ 87,6 91,6 @@ pub fn parse_timed_string(s: &str, diary_name: &str) -> Result<Timed> {
    } else {
        let s = format!("Cannot parse config: 'diary.diaries.{}.timed = {}'",
                        diary_name, s);
        Err(s).map_err(From::from)
        Err(format_err!("{}", s))
    }
}

M bin/domain/imag-habit/Cargo.toml => bin/domain/imag-habit/Cargo.toml +3 -2
@@ 25,9 25,10 @@ maintenance                       = { status     = "actively-developed" }
chrono = "0.4"
log = "0.4"
toml = "0.4"
toml-query = "0.7"
kairos = { git = "https://github.com/matthiasbeyer/kairos", branch = "master" }
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
kairos = { git = "https://github.com/matthiasbeyer/kairos", branch = "failure" }
prettytable-rs = "0.8"
failure = "0.1"

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

M bin/domain/imag-habit/src/main.rs => bin/domain/imag-habit/src/main.rs +5 -3
@@ 39,6 39,7 @@ extern crate toml_query;
extern crate kairos;
extern crate chrono;
extern crate prettytable;
#[macro_use] extern crate failure;

extern crate libimaghabit;
extern crate libimagstore;


@@ 53,6 54,7 @@ use std::process::exit;
use prettytable::Table;
use prettytable::Cell;
use prettytable::Row;
use failure::Error;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;


@@ 232,8 234,7 @@ fn delete(rt: &Runtime) {
// future flag. If it is true, the check will not be performed and it is assumed that `--future`
// was passed.
fn today(rt: &Runtime, future: bool) {
    use libimaghabit::error::ResultExt;
    use libimaghabit::error::HabitErrorKind as HEK;
    use failure::ResultExt;

    let (future, show_done) = {
        if !future {


@@ 296,7 297,8 @@ fn today(rt: &Runtime, future: bool) {
                am.value_of("today-show-next-n")
                    .map(|x| {
                        x.parse::<usize>()
                            .chain_err(|| HEK::from(format!("Cannot parse String '{}' to integer", x)))
                            .context(format_err!("Cannot parse String '{}' to integer", x))
                            .map_err(Error::from)
                            .map_err_trace_exit_unwrap(1)
                    })
            }).unwrap_or(5);

M bin/domain/imag-log/Cargo.toml => bin/domain/imag-log/Cargo.toml +2 -1
@@ 24,9 24,10 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
is-match = "0.1"
itertools = "0.7"
failure = "0.1"

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

M bin/domain/imag-log/src/main.rs => bin/domain/imag-log/src/main.rs +10 -5
@@ 38,6 38,7 @@ extern crate clap;
extern crate toml;
extern crate toml_query;
extern crate itertools;
extern crate failure;

extern crate libimaglog;
#[macro_use] extern crate libimagrt;


@@ 47,6 48,9 @@ extern crate libimagdiary;

use std::io::Write;

use failure::Error;
use failure::err_msg;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagerror::trace::MapErrTrace;


@@ 55,7 59,6 @@ use libimagerror::exit::ExitUnwrap;
use libimagerror::iter::TraceIterator;
use libimagdiary::diary::Diary;
use libimaglog::log::Log;
use libimaglog::error::LogError as LE;
use libimagstore::iter::get::StoreIdGetIteratorExtension;

mod ui;


@@ 175,16 178,17 @@ fn get_diary_name(rt: &Runtime) -> String {

    let cfg = rt
        .config()
        .ok_or(LE::from("Configuration not present, cannot continue"))
        .ok_or_else(|| Error::from(err_msg("Configuration not present, cannot continue")))
        .map_err_trace_exit_unwrap(1);

    let logs = cfg
        .read("log.logs")
        .map_err(Error::from)
        .map_err_trace_exit_unwrap(1)
        .ok_or(LE::from("Configuration missing: 'log.logs'"))
        .ok_or_else(|| Error::from(err_msg("Configuration missing: 'log.logs'")))
        .map_err_trace_exit_unwrap(1)
        .as_array()
        .ok_or(LE::from("Configuration 'log.logs' is not an Array"))
        .ok_or_else(|| Error::from(err_msg("Configuration 'log.logs' is not an Array")))
        .map_err_trace_exit_unwrap(1);

    if !logs.iter().all(|e| is_match!(e, &Value::String(_))) {


@@ 201,8 205,9 @@ fn get_diary_name(rt: &Runtime) -> String {

    let current_log = cfg
        .read_string("log.default")
        .map_err(Error::from)
        .map_err_trace_exit_unwrap(1)
        .ok_or(LE::from("Configuration missing: 'log.default'"))
        .ok_or_else(|| Error::from(err_msg("Configuration missing: 'log.default'")))
        .map_err_trace_exit_unwrap(1);

    if !logs.contains(&current_log) {

M bin/domain/imag-mail/Cargo.toml => bin/domain/imag-mail/Cargo.toml +1 -0
@@ 23,6 23,7 @@ maintenance                       = { status     = "actively-developed" }

[dependencies]
log = "0.4.0"
failure = "0.1"

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

M bin/domain/imag-mail/src/main.rs => bin/domain/imag-mail/src/main.rs +7 -3
@@ 34,6 34,7 @@

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

#[macro_use] extern crate libimagrt;
extern crate libimagmail;


@@ 42,6 43,9 @@ extern crate libimagutil;

use std::io::Write;

use failure::Error;
use failure::err_msg;

use libimagerror::trace::{MapErrTrace, trace_error};
use libimagerror::iter::TraceIterator;
use libimagerror::exit::ExitUnwrap;


@@ 91,8 95,7 @@ fn import_mail(rt: &Runtime) {
}

fn list(rt: &Runtime) {
    use libimagmail::error::MailErrorKind as MEK;
    use libimagmail::error::ResultExt;
    use failure::ResultExt;

        // TODO: Implement lister type in libimagmail for this
    fn list_mail(rt: &Runtime, m: Mail) {


@@ 149,7 152,8 @@ fn list(rt: &Runtime) {
        .filter_map(|id| {
            rt.store()
                .get(id)
                .chain_err(|| MEK::RefHandlingError)
                .context(err_msg("Ref handling error"))
                .map_err(Error::from)
                .map_err_trace_exit_unwrap(1)
                .map(|fle| Mail::from_fle(fle).map_err_trace().ok())
        })

M bin/domain/imag-timetrack/Cargo.toml => bin/domain/imag-timetrack/Cargo.toml +2 -1
@@ 27,7 27,8 @@ chrono = "0.4"
filters = "0.3"
itertools = "0.7"
prettytable-rs = "0.8"
kairos = { git = "https://github.com/matthiasbeyer/kairos", branch = "master" }
kairos = { git = "https://github.com/matthiasbeyer/kairos", branch = "failure" }
failure = "0.1"

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

M bin/domain/imag-timetrack/src/day.rs => bin/domain/imag-timetrack/src/day.rs +4 -4
@@ 22,13 22,13 @@ use std::str::FromStr;

use filters::filter::Filter;
use chrono::NaiveDateTime;
use failure::Error;

use libimagerror::trace::trace_error;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::io::ToExitCode;
use libimagstore::store::FileLockEntry;
use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::tag::TimeTrackingTag;


@@ 44,7 44,7 @@ pub fn day(rt: &Runtime) -> i32 {
    let filter = {
        let start = match cmd.value_of("start").map(NaiveDateTime::from_str) {
            None    => ::chrono::offset::Local::today().and_hms(0, 0, 0).naive_local(),
            Some(s) => match s.map_err(TTE::from) {
            Some(s) => match s.map_err(Error::from) {
                Ok(dt) => dt,
                Err(e) => {
                    trace_error(&e);


@@ 55,7 55,7 @@ pub fn day(rt: &Runtime) -> i32 {

        let end = match cmd.value_of("end").map(NaiveDateTime::from_str) {
            None    => ::chrono::offset::Local::today().and_hms(23, 59, 59).naive_local(),
            Some(s) => match s.map_err(TTE::from) {
            Some(s) => match s.map_err(Error::from) {
                Ok(dt) => dt,
                Err(e) => {
                    trace_error(&e);


@@ 91,7 91,7 @@ pub fn day(rt: &Runtime) -> i32 {
        .map_err_trace_exit_unwrap(1)
        .trace_unwrap()
        .filter(|e| filter.filter(e))
        .map(|e| -> Result<_, TTE> {
        .map(|e| -> Result<_, Error> {
            debug!("Processing {:?}", e.get_location());

            let tag   = e.get_timetrack_tag()?;

M bin/domain/imag-timetrack/src/list.rs => bin/domain/imag-timetrack/src/list.rs +7 -3
@@ 25,15 25,17 @@ use prettytable::Cell;
use kairos::parser::Parsed;
use kairos::parser::parse as kairos_parse;
use clap::ArgMatches;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;
use failure::Error;

use libimagerror::trace::trace_error;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagstore::store::FileLockEntry;
use libimagtimetrack::error::TimeTrackError;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::error::Result;

use libimagrt::runtime::Runtime;



@@ 63,6 65,7 @@ pub fn list(rt: &Runtime) -> i32 {
                ::std::process::exit(1)
            },
            Some(Err(e)) => {
                let e = Error::from(e);
                trace_error(&e);
                ::std::process::exit(1)
            }


@@ 164,7 167,8 @@ pub fn list_impl(rt: &Runtime,
        })
        .map_err_trace_exit_unwrap(1)
        .print(&mut rt.stdout())
        .map_err(|_| TimeTrackError::from(String::from("Failed printing table")))
        .context(err_msg("Failed to print table"))
        .map_err(Error::from)
        .map(|_| 0)
        .map_err_trace()
        .unwrap_or(1)

M bin/domain/imag-timetrack/src/main.rs => bin/domain/imag-timetrack/src/main.rs +1 -0
@@ 41,6 41,7 @@ extern crate filters;
extern crate itertools;
extern crate prettytable;
extern crate kairos;
extern crate failure;

extern crate libimagerror;
extern crate libimagstore;

M bin/domain/imag-timetrack/src/month.rs => bin/domain/imag-timetrack/src/month.rs +4 -4
@@ 22,13 22,13 @@ use std::str::FromStr;

use filters::filter::Filter;
use chrono::NaiveDateTime;
use failure::Error;

use libimagerror::trace::trace_error;
use libimagerror::trace::MapErrTrace;
use libimagerror::io::ToExitCode;
use libimagerror::iter::TraceIterator;
use libimagstore::store::FileLockEntry;
use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::tag::TimeTrackingTag;


@@ 48,7 48,7 @@ pub fn month(rt: &Runtime) -> i32 {

        let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) {
            None    => NaiveDate::from_ymd(now.year(), now.month(), 1).and_hms(0, 0, 0),
            Some(s) => match s.map_err(TTE::from) {
            Some(s) => match s.map_err(Error::from) {
                Ok(dt) => dt,
                Err(e) => {
                    trace_error(&e);


@@ 70,7 70,7 @@ pub fn month(rt: &Runtime) -> i32 {

                NaiveDate::from_ymd(year, month, 1).and_hms(0, 0, 0)
            },
            Some(s) => match s.map_err(TTE::from) {
            Some(s) => match s.map_err(Error::from) {
                Ok(dt) => dt,
                Err(e) => {
                    trace_error(&e);


@@ 106,7 106,7 @@ pub fn month(rt: &Runtime) -> i32 {
        .map_err_trace_exit_unwrap(1)
        .trace_unwrap()
        .filter(|e| filter.filter(e))
        .map(|e| -> Result<_, TTE> {
        .map(|e| -> Result<_, Error> {
            debug!("Processing {:?}", e.get_location());

            let tag   = e.get_timetrack_tag()?;

M bin/domain/imag-timetrack/src/start.rs => bin/domain/imag-timetrack/src/start.rs +2 -2
@@ 20,10 20,10 @@
use std::str::FromStr;

use chrono::naive::NaiveDateTime;
use failure::Error;

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;
use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::tag::TimeTrackingTag;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagerror::trace::MapErrTrace;


@@ 34,7 34,7 @@ pub fn start(rt: &Runtime) -> i32 {

    let start = match cmd.value_of("start-time") {
        None | Some("now") => ::chrono::offset::Local::now().naive_local(),
        Some(ndt)          => match NaiveDateTime::from_str(ndt).map_err(TTE::from) {
        Some(ndt)          => match NaiveDateTime::from_str(ndt).map_err(Error::from) {
            Ok(ndt) => ndt,
            Err(e) =>  {
                trace_error(&e);

M bin/domain/imag-timetrack/src/stop.rs => bin/domain/imag-timetrack/src/stop.rs +2 -2
@@ 21,13 21,13 @@ use std::str::FromStr;

use filters::filter::Filter;
use chrono::NaiveDateTime;
use failure::Error;

use libimagerror::trace::trace_error;
use libimagerror::iter::TraceIterator;
use libimagerror::trace::MapErrTrace;
use libimagrt::runtime::Runtime;

use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::tag::TimeTrackingTag;
use libimagtimetrack::timetrackingstore::*;


@@ 42,7 42,7 @@ pub fn stop(rt: &Runtime) -> i32 {

    let stop_time = match cmd.value_of("stop-time") {
        None | Some("now") => ::chrono::offset::Local::now().naive_local(),
        Some(ndt)          => match NaiveDateTime::from_str(ndt).map_err(TTE::from) {
        Some(ndt)          => match NaiveDateTime::from_str(ndt).map_err(Error::from) {
            Ok(ndt) => ndt,
            Err(e) =>  {
                trace_error(&e);

M bin/domain/imag-timetrack/src/track.rs => bin/domain/imag-timetrack/src/track.rs +3 -3
@@ 22,10 22,10 @@ use std::process::exit;
use clap::ArgMatches;
use chrono::naive::NaiveDate;
use chrono::naive::NaiveDateTime;
use failure::Error;

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;
use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::tag::TimeTrackingTag;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagerror::trace::MapErrTrace;


@@ 43,10 43,10 @@ pub fn track(rt: &Runtime) -> i32 {
        match cmd.value_of(clap_name) {
            Some("now") => Some(::chrono::offset::Local::now().naive_local()),
            Some(els) => {
                match NaiveDateTime::parse_from_str(els, DATE_TIME_PARSE_FMT).map_err(TTE::from) {
                match NaiveDateTime::parse_from_str(els, DATE_TIME_PARSE_FMT).map_err(Error::from) {
                    Ok(ndt) => Some(ndt),
                    Err(e_ndt) => {
                        match NaiveDate::parse_from_str(els, DATE_PARSE_FMT).map_err(TTE::from) {
                        match NaiveDate::parse_from_str(els, DATE_PARSE_FMT).map_err(Error::from) {
                            Ok(ndt) => Some(ndt.and_hms(0, 0, 0)),
                            Err(e_nd) => {
                                error!("Cannot parse date {}:", errname);

M bin/domain/imag-timetrack/src/week.rs => bin/domain/imag-timetrack/src/week.rs +4 -4
@@ 22,13 22,13 @@ use std::str::FromStr;

use filters::filter::Filter;
use chrono::NaiveDateTime;
use failure::Error;

use libimagerror::trace::trace_error;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::io::ToExitCode;
use libimagstore::store::FileLockEntry;
use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::tag::TimeTrackingTag;


@@ 50,7 50,7 @@ pub fn week(rt: &Runtime) -> i32 {
        let start = match cmd
            .value_of("start")
            .map(|s| {
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(TTE::from)
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(Error::from)
            })
        {
            None         => NaiveDate::from_isoywd(this_week.year(), this_week.week(), Weekday::Mon)


@@ 65,7 65,7 @@ pub fn week(rt: &Runtime) -> i32 {
        let end = match cmd
            .value_of("end")
            .map(|s| {
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(TTE::from)
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(Error::from)
            })
        {
            None         => NaiveDate::from_isoywd(this_week.year(), this_week.week(), Weekday::Sun)


@@ 104,7 104,7 @@ pub fn week(rt: &Runtime) -> i32 {
        .map_err_trace_exit_unwrap(1)
        .trace_unwrap()
        .filter(|e| filter.filter(e))
        .map(|e| -> Result<_, TTE> {
        .map(|e| -> Result<_, Error> {
            debug!("Processing {:?}", e.get_location());

            let tag   = e.get_timetrack_tag()?;

M bin/domain/imag-timetrack/src/year.rs => bin/domain/imag-timetrack/src/year.rs +4 -4
@@ 22,13 22,13 @@ use std::str::FromStr;

use filters::filter::Filter;
use chrono::NaiveDateTime;
use failure::Error;

use libimagerror::trace::trace_error;
use libimagerror::trace::MapErrTrace;
use libimagerror::iter::TraceIterator;
use libimagerror::io::ToExitCode;
use libimagstore::store::FileLockEntry;
use libimagtimetrack::error::TimeTrackError as TTE;
use libimagtimetrack::timetrackingstore::TimeTrackStore;
use libimagtimetrack::timetracking::TimeTracking;
use libimagtimetrack::tag::TimeTrackingTag;


@@ 49,7 49,7 @@ pub fn year(rt: &Runtime) -> i32 {
        let start = match cmd
            .value_of("start")
            .map(|s| {
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(TTE::from)
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(Error::from)
            })
        {
            None => NaiveDate::from_ymd(now.year(), 1, 1).and_hms(0, 0, 0),


@@ 63,7 63,7 @@ pub fn year(rt: &Runtime) -> i32 {
        let end = match cmd
            .value_of("end")
            .map(|s| {
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(TTE::from)
                ::chrono::naive::NaiveDateTime::from_str(s).map_err(Error::from)
            })
        {
            None => {


@@ 104,7 104,7 @@ pub fn year(rt: &Runtime) -> i32 {
        .map_err_trace_exit_unwrap(1)
        .trace_unwrap()
        .filter(|e| filter.filter(e))
        .map(|e| -> Result<_, TTE> {
        .map(|e| -> Result<_, Error> {
            debug!("Processing {:?}", e.get_location());

            let tag   = e.get_timetrack_tag()?;

M bin/domain/imag-todo/Cargo.toml => bin/domain/imag-todo/Cargo.toml +2 -1
@@ 24,8 24,9 @@ maintenance                       = { status     = "actively-developed" }
[dependencies]
log = "0.4.0"
toml = "0.4"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
is-match = "0.1"
failure  = "0.1"

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

M bin/domain/imag-todo/src/main.rs => bin/domain/imag-todo/src/main.rs +4 -2
@@ 37,6 37,7 @@ extern crate clap;
extern crate toml;
extern crate toml_query;
#[macro_use] extern crate is_match;
extern crate failure;

#[macro_use] extern crate libimagrt;
extern crate libimagerror;


@@ 45,6 46,7 @@ extern crate libimagtodo;
use std::process::{Command, Stdio};
use std::io::stdin;
use std::io::Write;
use failure::Error;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;


@@ 119,7 121,7 @@ fn list(rt: &Runtime) {
    // return Result<T> instead of Result<Option<T>>, which is a real inconvenience.
    //
    let no_identifier = |e: &::toml_query::error::Error| -> bool {
        is_match!(e.kind(), &::toml_query::error::ErrorKind::IdentifierNotFoundInDocument(_))
        is_match!(e, &::toml_query::error::Error::IdentifierNotFoundInDocument(_))
    };

    let res = rt.store().all_tasks() // get all tasks


@@ 136,7 138,7 @@ fn list(rt: &Runtime) {
                            },
                            Err(e) => {
                                if !no_identifier(&e) {
                                    trace_error(&e);
                                    trace_error(&Error::from(e));
                                }
                                None
                            }

M bin/domain/imag-wiki/src/main.rs => bin/domain/imag-wiki/src/main.rs +1 -1
@@ 48,7 48,7 @@ fn main() {
    let version = make_imag_version!();
    let rt = generate_runtime_setup("imag-wiki",
                                    &version,
                                    "Manage a personal Wiki",
                                    "Personal wiki",
                                    build_ui);

    let wiki_name = rt.cli().value_of("wikiname").unwrap_or("default");

M lib/core/libimagerror/Cargo.toml => lib/core/libimagerror/Cargo.toml +4 -3
@@ 20,6 20,7 @@ is-it-maintained-open-issues      = { repository = "matthiasbeyer/imag" }
maintenance                       = { status     = "actively-developed" }

[dependencies]
log         = "0.4"
ansi_term   = "0.11"
error-chain = "0.12"
log            = "0.4"
ansi_term      = "0.11"
failure        = "0.1"
failure_derive = "0.1"

A lib/core/libimagerror/src/errors.rs => lib/core/libimagerror/src/errors.rs +105 -0
@@ 0,0 1,105 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 the imag contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#[derive(Debug, Clone, Eq, PartialEq, Fail)]
pub enum ErrorMsg {
    #[fail(display = "IO Error")]
    IO,

    #[fail(display = "Locking error")]
    LockError,

    #[fail(display = "UTF8 error")]
    UTF8Error,

    #[fail(display = "Error in external process")]
    ExternalProcessError,

    #[fail(display = "File Error")]
    FileError,

    #[fail(display = "File not copied")]
    FileNotCopied,

    #[fail(display = "File not created")]
    FileNotCreated,

    #[fail(display = "File not found")]
    FileNotFound,

    #[fail(display = "Fail not removed")]
    FileNotRemoved,

    #[fail(display = "Fail not renamed")]
    FileNotRenamed,

    #[fail(display = "File not seeked")]
    FileNotSeeked,

    #[fail(display = "File not written")]
    FileNotWritten,

    #[fail(display = "Directory not created")]
    DirNotCreated,


    #[fail(display = "Formatting error")]
    FormatError,


    #[fail(display = "ID is locked")]
    IdLocked,

    #[fail(display = "Error while converting values")]
    ConversionError,


    #[fail(display = "Entry exists already: {}", _0)]
    EntryAlreadyExists(String),

    #[fail(display = "Entry not found: {}", _0)]
    EntryNotFound(String),

    #[fail(display = "Entry header error")]
    EntryHeaderError,

    #[fail(display = "Entry header type error")]
    EntryHeaderTypeError,

    #[fail(display = "Entry header type error at '{}', expected '{}'", _0, _1)]
    EntryHeaderTypeError2(&'static str, &'static str),

    #[fail(display = "Entry header read error")]
    EntryHeaderReadError,

    #[fail(display = "Entry header write error")]
    EntryHeaderWriteError,

    #[fail(display = "Entry header field missing: {}", _0)]
    EntryHeaderFieldMissing(&'static str),


    #[fail(display = "Toml deserialization error")]
    TomlDeserError,

    #[fail(display = "Toml querying error")]
    TomlQueryError,

}


M lib/core/libimagerror/src/iter.rs => lib/core/libimagerror/src/iter.rs +28 -82
@@ 17,7 17,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use error_chain::ChainedError;
use failure::Error;

/// An iterator that unwraps the `Ok` items of `iter`, while passing the `Err` items to its
/// closure `f`.


@@ 31,9 31,10 @@ pub struct UnwrapWith<I, F>{
    f: F
}

impl<I, F, T, E> Iterator for UnwrapWith<I, F> where
    I: Iterator<Item = Result<T, E>>,
    F: FnMut(E)
impl<I, F, T> Iterator for UnwrapWith<I, F>
    where
        I: Iterator<Item = Result<T, Error>>,
        F: FnMut(Error)
{
    type Item = T;



@@ 41,14 42,13 @@ impl<I, F, T, E> Iterator for UnwrapWith<I, F> where
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.iter.next() {
                Some(Err(e)) => {
                    (self.f)(e);
                },
                Some(Err(e))   => { (self.f)(e); },
                Some(Ok(item)) => return Some(item),
                None => return None,
                None           => return None,
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, upper) = self.iter.size_hint();


@@ 56,32 56,14 @@ impl<I, F, T, E> Iterator for UnwrapWith<I, F> where
    }
}

impl<I, F, T, E> DoubleEndedIterator for UnwrapWith<I, F> where
    I: DoubleEndedIterator<Item = Result<T, E>>,
    F: FnMut(E)
{
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        loop {
            match self.iter.next_back() {
                Some(Err(e)) => {
                    (self.f)(e);
                },
                Some(Ok(item)) => return Some(item),
                None => return None,
            }
        }
    }
}


/// Iterator helper for Unwrap with exiting on error
pub struct UnwrapExit<I, T, E>(I, i32)
    where I: Iterator<Item = Result<T, E>>,
          E: ChainedError;
pub struct UnwrapExit<I, T>(I, i32)
    where I: Iterator<Item = Result<T, Error>>;

impl<I, T, E> Iterator for UnwrapExit<I, T, E>
    where I: Iterator<Item = Result<T, E>>,
          E: ChainedError
impl<I, T> Iterator for UnwrapExit<I, T>
    where I: Iterator<Item = Result<T, Error>>,
{
    type Item = T;



@@ 91,9 73,8 @@ impl<I, T, E> Iterator for UnwrapExit<I, T, E>
    }
}

impl<I, T, E> DoubleEndedIterator for UnwrapExit<I, T, E>
    where I: DoubleEndedIterator<Item = Result<T, E>>,
          E: ChainedError
impl<I, T> DoubleEndedIterator for UnwrapExit<I, T>
    where I: DoubleEndedIterator<Item = Result<T, Error>>,
{
    fn next_back(&mut self) -> Option<Self::Item> {
        use trace::MapErrTrace;


@@ 102,18 83,21 @@ impl<I, T, E> DoubleEndedIterator for UnwrapExit<I, T, E>
}

/// This trait provides methods that make it easier to work with iterators that yield a `Result`.
pub trait TraceIterator<T, E> : Iterator<Item = Result<T, E>> + Sized {
    /// Creates an iterator that yields the item in each `Ok` item, while filtering out the `Err`
pub trait TraceIterator<T> : Iterator<Item = Result<T, Error>> + Sized {
    /// Creates an iterator that yields the item in each `Ok` item, while filtering out the
    /// `Err`
    /// items. Each filtered `Err` will be trace-logged with [`::trace::trace_error`].
    ///
    /// As with all iterators, the processing is lazy. If you do not use the result of this method,
    /// nothing will be passed to `::trace::trace_error`, no matter how many `Err` items might
    /// be present.
    #[inline]
    fn trace_unwrap<K>(self) -> UnwrapWith<Self, fn(E)> where E: ChainedError<ErrorKind = K> {
    fn trace_unwrap(self) -> UnwrapWith<Self, fn(Error)> {
        #[inline]
        fn trace_error<K, E: ChainedError<ErrorKind = K>>(err: E) {
            eprintln!("{}", err.display_chain());
        fn trace_error(err: Error) {
            err.iter_chain().for_each(|cause| {
                eprintln!("{}", cause);
            });
        }

        self.unwrap_with(trace_error)


@@ 127,12 111,11 @@ pub trait TraceIterator<T, E> : Iterator<Item = Result<T, E>> + Sized {
    /// nothing will be passed to `::trace::trace_error_exit`, no matter how many `Err` items might
    /// be present.
    #[inline]
    fn trace_unwrap_exit(self, exitcode: i32) -> UnwrapExit<Self, T, E>
        where E: ChainedError
    {
    fn trace_unwrap_exit(self, exitcode: i32) -> UnwrapExit<Self, T> {
        UnwrapExit(self, exitcode)
    }


    /// Takes a closure and creates an iterator that will yield the items inside all `Ok` items
    /// yielded by the original iterator. All `Err` items will be filtered out, and the contents
    /// of each `Err` will be passed to the closure.


@@ 141,50 124,13 @@ pub trait TraceIterator<T, E> : Iterator<Item = Result<T, E>> + Sized {
    /// for the closure to be called.
    #[inline]
    fn unwrap_with<F>(self, f: F) -> UnwrapWith<Self, F>
        where F: FnMut(E)
        where F: FnMut(Error)
    {
        UnwrapWith { iter: self, f }
    }
}

impl<I, T, E> TraceIterator<T, E> for I where
    I: Iterator<Item = Result<T, E>>
impl<I, T> TraceIterator<T> for I where
    I: Iterator<Item = Result<T, Error>>
{}

#[cfg(test)]
mod test {
    use super::TraceIterator;

    #[derive(Copy, Clone, Eq, PartialEq, Debug)]
    struct TestError(i32);

    #[test]
    fn test_unwrap_with() {
        let original = vec![Ok(1), Err(TestError(2)), Ok(3), Err(TestError(4))];
        let mut errs = vec![];

        let oks = original
            .into_iter()
            .unwrap_with(|e|errs.push(e))
            .collect::<Vec<_>>();

        assert_eq!(&oks, &[1, 3]);
        assert_eq!(&errs, &[TestError(2), TestError(4)]);
    }

    #[test]
    fn test_unwrap_with_backward() {
        let original = vec![Ok(1), Err(TestError(2)), Ok(3), Err(TestError(4))];
        let mut errs = vec![];

        let oks = original
            .into_iter()
            .rev()
            .unwrap_with(|e|errs.push(e))
            .collect::<Vec<_>>();

        assert_eq!(&oks, &[3, 1]);
        assert_eq!(&errs, &[TestError(4), TestError(2)]);
    }

}

M lib/core/libimagerror/src/lib.rs => lib/core/libimagerror/src/lib.rs +5 -3
@@ 35,11 35,13 @@

#[macro_use] extern crate log;
extern crate ansi_term;
extern crate error_chain;
extern crate failure;
#[macro_use] extern crate failure_derive;

pub mod io;
pub mod errors;
pub mod exit;
pub mod trace;
pub mod io;
pub mod iter;
pub mod str;
pub mod trace;


M lib/core/libimagerror/src/trace.rs => lib/core/libimagerror/src/trace.rs +17 -15
@@ 21,7 21,7 @@ use std::process::exit;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use error_chain::ChainedError;
use failure::Error;
use ansi_term::Colour::Red;

struct ImagTrace<'a, T: 'a + ?Sized>(&'a T);


@@ 32,32 32,34 @@ impl<'a, T: 'a + ?Sized> ImagTrace<'a, T> {
    }
}

impl<'a, T> Display for ImagTrace<'a, T>
    where T: ChainedError
impl<'a> Display for ImagTrace<'a, Error>
{
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        try!(writeln!(fmt, "{}: {}", Red.blink().paint("ERROR[   0]"), self.0));

        for (i, e) in self.0.iter().enumerate().skip(1) {
            try!(writeln!(fmt, "{}: {}", Red.blink().paint(format!("ERROR[{:>4}]", i)), e));
        let _ = writeln!(fmt, "{}: {}", Red.blink().paint("ERROR[   0]"), self.0)?;

        {
            for (i, cause) in self.0.iter_causes().enumerate() {
                let _ = writeln!(fmt,
                                 "{prefix}: {error}",
                                 prefix = Red.blink().paint(format!("ERROR[{:>4}]", i)),
                                 error = cause)?;
            }
        }

        if let Some(backtrace) = self.0.backtrace() {
            try!(writeln!(fmt, "{}", Red.paint("--- BACKTRACE ---")));
            try!(writeln!(fmt, "{:?}", backtrace));
        }
        let _ = writeln!(fmt, "{}", Red.paint("--- BACKTRACE ---"))?;
        let _ = writeln!(fmt, "{:?}", self.0.backtrace())?;

        Ok(())
    }
}


pub fn trace_error<K, C: ChainedError<ErrorKind = K>>(e: &C) {
pub fn trace_error(e: &Error) {
    eprintln!("{}", ImagTrace::new(e));
}

pub fn trace_error_dbg<K, C: ChainedError<ErrorKind = K>>(e: &C) {
    debug!("{}", e.display_chain());
pub fn trace_error_dbg(e: &Error) {
    debug!("{}", ImagTrace::new(e));
}

/// Helper functions for `Result<T, E>` types to reduce overhead in the following situations:


@@ 74,7 76,7 @@ pub trait MapErrTrace {
    fn map_err_trace_exit_unwrap(self, code: i32) -> Self::Output;
}

impl<U, K, E: ChainedError<ErrorKind = K>> MapErrTrace for Result<U, E> {
impl<U> MapErrTrace for Result<U, Error> {
    type Output = U;

    /// Simply call `trace_error()` on the Err (if there is one) and return the error.

M lib/core/libimagrt/Cargo.toml => lib/core/libimagrt/Cargo.toml +3 -3
@@ 25,10 25,10 @@ toml = "0.4"
xdg-basedir = "1.0"
itertools = "0.7"
ansi_term = "0.11"
is-match = "0.1"
toml-query = "0.7"
error-chain = "0.12"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
atty = "0.2"
failure        = "0.1"
failure_derive = "0.1"

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

M lib/core/libimagrt/src/configuration.rs => lib/core/libimagrt/src/configuration.rs +15 -15
@@ 21,20 21,19 @@ use std::path::PathBuf;

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

use error::RuntimeError as RE;
use error::RuntimeErrorKind as REK;
use error::Result;
use error::ResultExt;
use libimagerror::errors::ErrorMsg as EM;

/// Get a new configuration object.
///
/// The passed runtimepath is used for searching the configuration file, whereas several file
/// names are tested. If that does not work, the home directory and the XDG basedir are tested
/// with all variants.
///
/// If that doesn't work either, an error is returned.
pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
pub fn fetch_config(searchpath: &PathBuf) -> Result<Option<Value>> {
    use std::env;
    use std::fs::File;
    use std::io::Read;


@@ 65,7 64,7 @@ pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
                                    .unwrap_or(vec![]),
    ];

    Itertools::flatten(vals.iter())
    let config = Itertools::flatten(vals.iter())
        .filter(|path| path.exists() && path.is_file())
        .filter_map(|path| {
            let content = {


@@ 90,13 89,14 @@ pub fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
                        .unwrap_or_else(|| String::from("Line unknown, Column unknown"));

                    let _ = write!(stderr(), "Config file parser error at {}", line_col);
                    let e : RE = RE::from(e);
                    let e = Error::from(EM::TomlDeserError);
                    trace_error(&e);
                    None
                })
        })
        .nth(0)
        .ok_or(RE::from_kind(REK::ConfigNoConfigFileFound))
        .nth(0);

    Ok(config)
}

/// Override the configuration.


@@ 120,16 120,16 @@ pub fn override_config(val: &mut Value, v: Vec<String>) -> Result<()> {
        .map(|(k, v)| {
            let value = val
                .read(&k)
                .chain_err(|| REK::ConfigTOMLParserError)?
                .ok_or(RE::from_kind(REK::ConfigOverrideKeyNotAvailable))?;
                .context(EM::TomlQueryError)?
                .ok_or_else(|| Error::from(err_msg("Confit parser error")))?;

            into_value(value, v)
                .map(|v| info!("Successfully overridden: {} = {}", k, v))
                .ok_or_else(|| RE::from_kind(REK::ConfigOverrideTypeNotMatching))
                .ok_or_else(|| Error::from(err_msg("Config override type not matching")))
        });

    for elem in iter {
        let _ = try!(elem.chain_err(|| REK::ConfigOverrideError));
        let _ = elem.context(err_msg("Config override error"))?;
    }

    Ok(())

D lib/core/libimagrt/src/error.rs => lib/core/libimagrt/src/error.rs +0 -125
@@ 1,125 0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

error_chain! {
    types {
        RuntimeError, RuntimeErrorKind, ResultExt, Result;
    }

    foreign_links {
        IO(::std::io::Error);
        TomlDeError(::toml::de::Error);
        TomlQueryError(::toml_query::error::Error);
        HandlebarsTemplateError(::handlebars::TemplateError);
    }

    errors {
        Instantiate {
            description("Could not instantiate")
            display("Could not instantiate")
        }

        IOError {
            description("IO Error")
            display("IO Error")
        }

        ProcessExitFailure {
            description("Process exited with failure")
            display("Process exited with failure")
        }

        IOLogFileOpenError {
            description("IO Error: Could not open logfile")
            display("IO Error: Could not open logfile")
        }

        ConfigTypeError(path: String, should_be_type: &'static str) {
            description("Error while reading the configuration: Type Error")
            display("Type Error: '{}' should be '{}'", path, should_be_type)
        }

        GlobalLogLevelConfigMissing {
            description("Global config 'imag.logging.level' missing")
            display("Global config 'imag.logging.level' missing")
        }

        GlobalDestinationConfigMissing {
            description("Global config 'imag.logging.destinations' missing")
            display("Global config 'imag.logging.destinations' missing")
        }

        InvalidLogLevelSpec {
            description("Invalid log level specification: Only 'trace', 'debug', 'info', 'warn', 'error' are allowed")
            display("Invalid log level specification: Only 'trace', 'debug', 'info', 'warn', 'error' are allowed")
        }

        ConfigMissingLoggingFormatTrace {
            description("Missing config for logging format for trace logging")
            display("Missing config for logging format for trace logging")
        }

        ConfigMissingLoggingFormatDebug {
            description("Missing config for logging format for debug logging")
            display("Missing config for logging format for debug logging")
        }

        ConfigMissingLoggingFormatInfo {
            description("Missing config for logging format for info logging")
            display("Missing config for logging format for info logging")
        }

        ConfigMissingLoggingFormatWarn {
            description("Missing config for logging format for warn logging")
            display("Missing config for logging format for warn logging")
        }

        ConfigMissingLoggingFormatError {
            description("Missing config for logging format for error logging")
            display("Missing config for logging format for error logging")
        }

        ConfigTOMLParserError {
            description("Configuration: TOML Parsing error")
            display("Configuration: TOML Parsing error")
        }

        ConfigNoConfigFileFound   {
            description("Configuration: No config file found")
            display("Configuration: No config file found")
        }

        ConfigOverrideError {
            description("Configuration: Config override error")
            display("Configuration: Config override error")
        }

        ConfigOverrideKeyNotAvailable {
            description("Configuration: Key not available")
            display("Configuration: Key not available")
        }

        ConfigOverrideTypeNotMatching {
            description("Configuration: Configuration Type not matching")
            display("Configuration: Configuration Type not matching")
        }

    }
}


M lib/core/libimagrt/src/lib.rs => lib/core/libimagrt/src/lib.rs +1 -3
@@ 36,17 36,16 @@
)]

#[macro_use] extern crate log;
#[macro_use] extern crate error_chain;
extern crate itertools;
#[cfg(unix)] extern crate xdg_basedir;
extern crate env_logger;
extern crate ansi_term;
extern crate handlebars;
#[macro_use] extern crate failure;

extern crate clap;
extern crate toml;
extern crate toml_query;
#[macro_use] extern crate is_match;
extern crate atty;

extern crate libimagstore;


@@ 54,7 53,6 @@ extern crate libimagutil;
extern crate libimagerror;
extern crate libimaginteraction;

pub mod error;
pub mod configuration;
pub mod logger;
pub mod io;

M lib/core/libimagrt/src/logger.rs => lib/core/libimagrt/src/logger.rs +90 -83
@@ 24,11 24,12 @@ use std::sync::Arc;
use std::sync::Mutex;
use std::ops::Deref;

use error::RuntimeErrorKind as EK;
use error::RuntimeError as RE;
use error::ResultExt;
use runtime::Runtime;

use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use clap::ArgMatches;
use log::{Log, Level, Record, Metadata};
use toml::Value;


@@ 36,10 37,10 @@ use toml_query::read::TomlValueReadExt;
use toml_query::read::TomlValueReadTypeExt;
use handlebars::Handlebars;

use libimagerror::errors::ErrorMsg as EM;

type ModuleName = String;
type Result<T> = ::std::result::Result<T, RE>;

#[derive(Debug)]
enum LogDestination {
    Stderr,
    File(Arc<Mutex<::std::fs::File>>),


@@ 51,7 52,6 @@ impl Default for LogDestination {
    }
}

#[derive(Debug)]
struct ModuleSettings {
    enabled:        bool,
    level:          Option<Level>,


@@ 89,32 89,39 @@ impl ImagLogger {

        {
            let fmt = aggregate_global_format_trace(config)?;
            handlebars.register_template_string("TRACE", fmt)?; // name must be uppercase
            handlebars.register_template_string("TRACE", fmt)
                .map_err(Error::from)
                .context(err_msg("Handlebars template error"))?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_debug(config)?;
            handlebars.register_template_string("DEBUG", fmt)?; // name must be uppercase
            handlebars.register_template_string("DEBUG", fmt)
                .map_err(Error::from)
                .context(err_msg("Handlebars template error"))?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_info(config)?;
            handlebars.register_template_string("INFO", fmt)?; // name must be uppercase
            handlebars.register_template_string("INFO", fmt)
                .map_err(Error::from)
                .context(err_msg("Handlebars template error"))?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_warn(config)?;
            handlebars.register_template_string("WARN", fmt)?; // name must be uppercase
            handlebars.register_template_string("WARN", fmt)
                .map_err(Error::from)
                .context(err_msg("Handlebars template error"))?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_error(config)?;
            handlebars.register_template_string("ERROR", fmt)?; // name must be uppercase
            handlebars.register_template_string("ERROR", fmt)
                .map_err(Error::from)
                .context(err_msg("Handlebars template error"))?; // name must be uppercase
        }

        let module_settings = aggregate_module_settings(matches, config)?;
        eprintln!("Logging: {:?}", module_settings);

        Ok(ImagLogger {
            global_loglevel     : aggregate_global_loglevel(matches, config)?,
            global_destinations : aggregate_global_destinations(matches, config)?,
            module_settings     : module_settings,
            module_settings     : aggregate_module_settings(matches, config)?,
            handlebars          : handlebars,
        })
    }


@@ 179,35 186,28 @@ impl Log for ImagLogger {
        self.module_settings
            .get(record_target)
            .map(|module_setting| {
                // We have a module setting some
                // * Check whether logging is enabled for this module and
                // * check whether the module logging level is >= or, if there is no module logging
                // level,
                // * check whether the global logging level is >= the record level.
                let set = module_setting.enabled &&
                    module_setting.level.unwrap_or(self.global_loglevel) >= record.level();

                if set { // if we want to log from a setting standpoint
                    // get the destinations for the module and log to all of them
                if set {
                    module_setting.destinations.as_ref().map(|destinations| for d in destinations {
                        let _ = log_to_destination(&d); // ignore errors, because what else?
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    });

                    // after that, log to the global destinations as well
                    for d in self.global_destinations.iter() {
                        let _ = log_to_destination(&d); // ignore errors, because what else?
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    }
                }
            })

        // if we do not have a setting for the record target
        .unwrap_or_else(|| {
            if self.global_loglevel >= record.level() { // if logging is enabled for that level
                self.global_destinations
                    .iter()
                    .for_each(|d| { // log to all global destinations
                        let _ = log_to_destination(&d); // ignore errors, because what else?
                    });
            if self.global_loglevel >= record.level() {
                // Yes, we log
                for d in self.global_destinations.iter() {
                    // If there's an error, we cannot do anything, can we?
                    let _ = log_to_destination(&d);
                }
            }
        });
    }


@@ 220,7 220,7 @@ fn match_log_level_str(s: &str) -> Result<Level> {
        "info"  => Ok(Level::Info),
        "warn"  => Ok(Level::Warn),
        "error" => Ok(Level::Error),
        _       => return Err(RE::from_kind(EK::InvalidLogLevelSpec)),
        lvl     => return Err(format_err!("Invalid logging level: {}", lvl)),
    }
}



@@ 243,8 243,10 @@ fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>) -> Re

    if let Some(cfg) = config {
        let cfg_loglevel = cfg
            .read_string("imag.logging.level")?
            .ok_or(RE::from_kind(EK::GlobalLogLevelConfigMissing))
            .read_string("imag.logging.level")
            .map_err(Error::from)
            .context(EM::TomlQueryError)?
            .ok_or(err_msg("Global log level config missing"))
            .and_then(|s| match_log_level_str(&s))?;

        if let Some(cli_loglevel) = get_arg_loglevel(matches)? {


@@ 273,7 275,9 @@ fn translate_destination(raw: &str) -> Result<LogDestination> {
                .map(Mutex::new)
                .map(Arc::new)
                .map(LogDestination::File)
                .chain_err(|| EK::IOLogFileOpenError)
                .map_err(Error::from)
                .context(EM::IO)
                .map_err(Error::from)
        }
    }
}


@@ 285,9 289,9 @@ fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
            acc.and_then(|mut v| {
                let dest = val.as_str()
                    .ok_or_else(|| {
                        let path = "imag.logging.modules.<mod>.destinations".to_owned();
                        let path = "imag.logging.modules.<mod>.destinations";
                        let ty   = "Array<String>";
                        RE::from_kind(EK::ConfigTypeError(path, ty))
                        Error::from(format_err!("Type error at {}, expected {}", path, ty))
                    })
                    .and_then(translate_destination)?;
                v.push(dest);


@@ 299,16 303,18 @@ fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
    -> Result<Vec<LogDestination>>
{
    let config_log_dest_path = "imag.logging.destinations";

    match config {
        Some(cfg) => cfg
            .read(&config_log_dest_path)?
            .ok_or_else(|| RE::from_kind(EK::GlobalDestinationConfigMissing))?
            .read("imag.logging.destinations")
            .map_err(Error::from)
            .context(EM::TomlQueryError)?
            .ok_or_else(|| err_msg("Global log destination config missing"))?
            .as_array()
            .ok_or_else(|| {
                let path = config_log_dest_path.to_owned();
                let path = "imag.logging.destinations";
                let ty   = "Array";
                RE::from_kind(EK::ConfigTypeError(path, ty))
                Error::from(format_err!("Type error at {}, expected {}", path, ty))
            })
            .and_then(translate_destinations),
        None => {


@@ 330,10 336,12 @@ fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
}

macro_rules! aggregate_global_format {
    ($read_str:expr, $error_kind_if_missing:expr, $config:expr) => {
        try!($config.ok_or(RE::from_kind($error_kind_if_missing)))
            .read_string($read_str)?
            .ok_or_else(|| RE::from_kind($error_kind_if_missing))
    ($read_str:expr, $error_msg_if_missing:expr, $config:expr) => {
        try!($config.ok_or_else(|| Error::from(err_msg($error_msg_if_missing))))
            .read_string($read_str)
            .map_err(Error::from)
            .context(EM::TomlQueryError)?
            .ok_or_else(|| Error::from(err_msg($error_msg_if_missing)))
    };
}



@@ 341,43 349,43 @@ fn aggregate_global_format_trace(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.trace",
                            EK::ConfigMissingLoggingFormatTrace,
                            config)
                             "Config missing: Logging format: Trace",
                             config)
}

fn aggregate_global_format_debug(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.debug",
                            EK::ConfigMissingLoggingFormatDebug,
                            config)
                             "Config missing: Logging format: Debug",
                             config)
}

fn aggregate_global_format_info(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.info",
                            EK::ConfigMissingLoggingFormatInfo,
                            config)
                             "Config missing: Logging format: Info",
                             config)
}

fn aggregate_global_format_warn(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.warn",
                            EK::ConfigMissingLoggingFormatWarn,
                            config)
                             "Config missing: Logging format: Warn",
                             config)
}

fn aggregate_global_format_error(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.error",
                            EK::ConfigMissingLoggingFormatError,
                            config)
                             "Config missing: Logging format: Error",
                             config)
}

fn aggregate_module_settings(matches: &ArgMatches, config: Option<&Value>)
fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
    -> Result<BTreeMap<ModuleName, ModuleSettings>>
{
    // Helper macro to return the error from Some(Err(_)) and map everything else to an


@@ 393,44 401,43 @@ fn aggregate_module_settings(matches: &ArgMatches, config: Option<&Value>)
    };

    match config {
        Some(cfg) => match cfg.read("imag.logging.modules") {
        Some(cfg) => match cfg.read("imag.logging.modules").map_err(Error::from) {
            Ok(Some(&Value::Table(ref t))) => {
                // translate the module settings from the table `t`
                let mut settings = BTreeMap::new();

                for (module_name, v) in t {
                    let destinations = inner_try! {
                        v.read("destinations")?
                        v.read("destinations")
                            .map_err(Error::from)
                            .context(EM::TomlQueryError)?
                            .map(|val| {
                                val.as_array()
                                    .ok_or_else(|| {
                                        let path = "imag.logging.modules.<mod>.destinations".to_owned();
                                        let path = "imag.logging.modules.<mod>.destinations";
                                        let ty = "Array";
                                        RE::from_kind(EK::ConfigTypeError(path, ty))
                                        Error::from(format_err!("Type error at {}, expected {}", path, ty))
                                    })
                                    .and_then(translate_destinations)
                            })
                    };


                    let (pre_enabled, level) = if matches.is_present(Runtime::arg_debugging_name()) {
                        (true, Some(Level::Debug))
                    } else {
                        let level = inner_try! {
                            v.read_string("level")?.map(|s| match_log_level_str(&s))
                        };

                        (false, level)
                    let level = inner_try! {
                        v.read_string("level")
                            .map_err(Error::from)
                            .context(EM::TomlQueryError)?
                            .map(|s| match_log_level_str(&s))
                    };

                    let enabled = pre_enabled ||
                        v.read("enabled")?
                            .map(|v| v.as_bool().unwrap_or(false))
                            .ok_or_else(|| {
                                let path = "imag.logging.modules.<mod>.enabled".to_owned();
                                let ty = "Boolean";
                                RE::from_kind(EK::ConfigTypeError(path, ty))
                            })?;
                    let enabled = v.read("enabled")
                        .map_err(Error::from)
                        .context(EM::TomlQueryError)?
                        .map(|v| v.as_bool().unwrap_or(false))
                        .ok_or_else(|| {
                            let path = "imag.logging.modules.<mod>.enabled";
                            let ty = "Boolean";
                            Error::from(format_err!("Type error at {}, expected {}", path, ty))
                        })?;

                    let module_settings = ModuleSettings {
                        enabled: enabled,


@@ 445,15 452,15 @@ fn aggregate_module_settings(matches: &ArgMatches, config: Option<&Value>)
                Ok(settings)
            },
            Ok(Some(_)) => {
                let path = "imag.logging.modules".to_owned();
                let path = "imag.logging.modules";
                let ty = "Table";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                Err(Error::from(format_err!("Type error at {}, expected {}", path, ty)))
            },
            Ok(None)    => {
                // No modules configured. This is okay!
                Ok(BTreeMap::new())
            },
            Err(e) => Err(e).map_err(From::from),
            Err(e) => Err(e).context(EM::TomlQueryError).map_err(Error::from),
        },
        None => {
            write!(stderr(), "No Configuration.").ok();

M lib/core/libimagrt/src/runtime.rs => lib/core/libimagrt/src/runtime.rs +25 -28
@@ 30,14 30,16 @@ use toml::Value;
use toml_query::read::TomlValueReadExt;

use clap::{Arg, ArgMatches};
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;

use configuration::{fetch_config, override_config, InternalConfiguration};
use error::RuntimeError;
use error::RuntimeErrorKind;
use error::ResultExt;
use logger::ImagLogger;
use io::OutputProxy;

use libimagerror::errors::ErrorMsg as EM;
use libimagerror::trace::*;
use libimagstore::store::Store;
use libimagstore::file_abstraction::InMemoryFileAbstraction;


@@ 62,7 64,7 @@ impl<'a> Runtime<'a> {
    /// and builds the Runtime object with it.
    ///
    /// The cli_app object should be initially build with the ::get_default_cli_builder() function.
    pub fn new<C>(cli_app: C) -> Result<Runtime<'a>, RuntimeError>
    pub fn new<C>(cli_app: C) -> Result<Runtime<'a>>
        where C: Clone + CliSpec<'a> + InternalConfiguration
    {
        use libimagerror::trace::trace_error;


@@ 76,17 78,15 @@ impl<'a> Runtime<'a> {

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

        let config = match fetch_config(&configpath) {
            Err(e) => if !is_match!(e.kind(), &RuntimeErrorKind::ConfigNoConfigFileFound) {
                return Err(e).chain_err(|| RuntimeErrorKind::Instantiate);
            } else {
                eprintln!("No config file found.");
                eprintln!("Maybe try to use 'imag-init' to initialize imag?");
                eprintln!("Continuing without configuration file");
                None
        let config = match fetch_config(&configpath)? {
            None => {
                return Err(err_msg("No configuration file found"))
                    .context(err_msg("Maybe try to use 'imag-init' to initialize imag?"))
                    .context(err_msg("Continuing without configuration file"))
                    .context(err_msg("Cannot instantiate runtime"))
                    .map_err(Error::from);
            },

            Ok(mut config) => {
            Some(mut config) => {
                if let Err(e) = override_config(&mut config, get_override_specs(&matches)) {
                    error!("Could not apply config overrides");
                    trace_error(&e);


@@ 102,16 102,14 @@ impl<'a> Runtime<'a> {
    }

    /// Builds the Runtime object using the given `config`.
    pub fn with_configuration<C>(cli_app: C, config: Option<Value>)
                                 -> Result<Runtime<'a>, RuntimeError>
    pub fn with_configuration<C>(cli_app: C, config: Option<Value>) -> Result<Runtime<'a>>
        where C: Clone + CliSpec<'a> + InternalConfiguration
    {
        let matches = cli_app.clone().matches();
        Runtime::_new(cli_app, matches, config)
    }

    fn _new<C>(cli_app: C, matches: ArgMatches<'a>, config: Option<Value>)
               -> Result<Runtime<'a>, RuntimeError>
    fn _new<C>(cli_app: C, matches: ArgMatches<'a>, config: Option<Value>) -> Result<Runtime<'a>>
    where C: Clone + CliSpec<'a> + InternalConfiguration
    {
        if cli_app.enable_logging() {


@@ 146,7 144,8 @@ impl<'a> Runtime<'a> {
                store: store,
            }
        })
        .chain_err(|| RuntimeErrorKind::Instantiate)
        .context(err_msg("Cannot instantiate runtime"))
        .map_err(Error::from)
    }

    ///


@@ 379,7 378,7 @@ impl<'a> Runtime<'a> {
    }

    /// Get a editor command object which can be called to open the $EDITOR
    pub fn editor(&self) -> Result<Option<Command>, RuntimeError> {
    pub fn editor(&self) -> Result<Option<Command>> {
        self.cli()
            .value_of("editor")
            .map(String::from)


@@ 391,7 390,7 @@ impl<'a> Runtime<'a> {
                    })
            })
            .or(env::var("EDITOR").ok())
            .ok_or_else(|| RuntimeErrorKind::IOError.into())
            .ok_or_else(|| Error::from(EM::IO))
            .map_dbg(|s| format!("Editing with '{}'", s))
            .and_then(|s| {
                let mut split = s.split_whitespace();


@@ 401,7 400,7 @@ impl<'a> Runtime<'a> {
                }
                let mut c = Command::new(command.unwrap()); // secured above
                c.args(split);
                c.stdin(::std::fs::File::open("/dev/tty")?);
                c.stdin(::std::fs::File::open("/dev/tty").context(EM::IO)?);
                c.stderr(::std::process::Stdio::inherit());
                Ok(Some(c))
            })


@@ 442,8 441,6 @@ impl<'a> Runtime<'a> {
    /// # Return value
    ///
    /// On success, the exit status object of the `Command` invocation is returned.
    /// On Error, a RuntimeError object is returned. This is also the case if writing the error
    /// message does not work.
    ///
    /// # Details
    ///


@@ 457,7 454,7 @@ impl<'a> Runtime<'a> {
                                                    command: S,
                                                    subcommand: S,
                                                    args: &ArgMatches)
        -> Result<::std::process::ExitStatus, RuntimeError>
        -> Result<::std::process::ExitStatus>
    {
        use std::io::Write;
        use std::io::ErrorKind;


@@ 465,8 462,7 @@ impl<'a> Runtime<'a> {
        let rtp_str = self.rtp()
            .to_str()
            .map(String::from)
            .ok_or(RuntimeErrorKind::IOError)
            .map_err(RuntimeError::from_kind)?;
            .ok_or_else(|| Error::from(EM::IO))?;

        let command = format!("{}-{}", command.as_ref(), subcommand.as_ref());



@@ 497,7 493,8 @@ impl<'a> Runtime<'a> {
                },
                _ => e,
            })
            .map_err(RuntimeError::from)
            .context(EM::IO)
            .map_err(Error::from)
    }
}


M lib/core/libimagstore/Cargo.toml => lib/core/libimagstore/Cargo.toml +2 -2
@@ 29,8 29,8 @@ walkdir = "2"
is-match = "0.1"
serde = "1"
serde_json = "1"
error-chain = "0.12"
toml-query = "0.7"
toml-query = { git = "https://github.com/matthiasbeyer/toml-query", branch = "failure" }
failure        = "0.1"

libimagerror = { version = "0.9.0", path = "../../../lib/core/libimagerror" }
libimagutil  = { version = "0.9.0", path = "../../../lib/etc/libimagutil" }

M lib/core/libimagstore/src/configuration.rs => lib/core/libimagstore/src/configuration.rs +9 -4
@@ 19,9 19,11 @@

use toml::Value;

use store::Result;
use error::StoreError as SE;
use error::StoreErrorKind as SEK;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;

use libimagerror::errors::ErrorMsg as EM;

/// Checks whether the store configuration has a key "implicit-create" which maps to a boolean
/// value. If that key is present, the boolean is returned, otherwise false is returned.


@@ 31,7 33,10 @@ pub fn config_implicit_store_create_allowed(config: &Option<Value>) -> Result<bo
    let key = "store.implicit-create";

    if let Some(ref t) = *config {
        t.read_bool(key)?.ok_or_else(|| SE::from_kind(SEK::ConfigKeyMissingError(key)))
        t.read_bool(key)
            .map_err(Error::from)
            .context(EM::TomlQueryError)?
            .ok_or_else(|| format_err!("Config key missing: {}", key))
    } else {
        Ok(false)
    }

D lib/core/libimagstore/src/error.rs => lib/core/libimagstore/src/error.rs +0 -253
@@ 1,253 0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;

use storeid::StoreId;

error_chain! {
    types {
        StoreError, StoreErrorKind, ResultExt, Result;
    }

    foreign_links {
        Io(::std::io::Error);
        Fmt(::std::fmt::Error);
        TomlDeserError(::toml::de::Error);
        TomlSerError(::toml::ser::Error);
        GlobPatternError(::glob::PatternError);
        TomlQueryError(::toml_query::error::Error);
    }

    errors {

        ConfigurationError      {
            description("Store Configuration Error")
            display("Store Configuration Error")
        }

        ConfigTypeError(key: &'static str, expected: &'static str) {
            description("Store configuration type error")
            display("Store configuration type error at '{}', expected {}", key, expected)
        }

        ConfigKeyMissingError(key: &'static str) {
            description("Configuration Key missing")
            display("Configuration Key missing: '{}'", key)
        }

        VersionError            {
            description("Incompatible store versions detected")
            display("Incompatible store versions detected")
        }

        CreateStoreDirDenied    {
            description("Creating store directory implicitely denied")
            display("Creating store directory implicitely denied")
        }

        FileError               {
            description("File Error")
            display("File Error")
        }

        IoError                 {
            description("IO Error")
            display("IO Error")
        }

        IdLocked                {
            description("ID locked")
            display("ID locked")
        }

        IdNotFound(sid: StoreId) {
            description("ID not found")
            display("ID not found: {}", sid)
        }

        FileNotFound            {
            description("File corresponding to ID not found")
            display("File corresponding to ID not found")
        }

        FileNotCreated          {
            description("File corresponding to ID could not be created")
            display("File corresponding to ID could not be created")
        }

        FileNotWritten          {
            description("File corresponding to ID could not be written to")
            display("File corresponding to ID could not be written to")
        }

        FileNotSeeked           {
            description("File corresponding to ID could not be seeked")
            display("File corresponding to ID could not be seeked")
        }

        FileNotRemoved          {
            description("File corresponding to ID could not be removed")
            display("File corresponding to ID could not be removed")
        }

        FileNotRenamed          {
            description("File corresponding to ID could not be renamed")
            display("File corresponding to ID could not be renamed")
        }

        FileNotCopied           {
            description("File could not be copied")
            display("File could not be copied")
        }

        DirNotCreated           {
            description("Directory/Directories could not be created")
            display("Directory/Directories could not be created")
        }

        StorePathExists(pb: PathBuf) {
            description("Store path exists")
            display("Store path exists: {:?}", pb)
        }

        StorePathCreate(pb: PathBuf) {
            description("Store path create")
            display("Store path create: {:?}", pb)
        }

        LockError               {
            description("Error locking datastructure")
            display("Error locking datastructure")
        }

        LockPoisoned            {
            description("The internal Store Lock has been poisoned")
            display("The internal Store Lock has been poisoned")
        }

        EntryAlreadyBorrowed(id: StoreId) {
            description("Entry is already borrowed")
            display("Entry is already borrowed: {:?}", id)
        }

        EntryAlreadyExists(id: StoreId) {
            description("Entry already exists")
            display("Entry already exists: {:?}", id)
        }

        MalformedEntry          {
            description("Entry has invalid formatting, missing header")
            display("Entry has invalid formatting, missing header")
        }

        HeaderTypeFailure       {
            description("Header type is wrong")
            display("Header type is wrong")
        }

        EncodingError           {
            description("Encoding error")
            display("Encoding error")
        }

        EntryRenameError(old: PathBuf, new: PathBuf) {
            description("Entry rename error")
            display("Entry rename error: {:?} -> {:?}", old, new)
        }

        StoreIdHandlingError    {
            description("StoreId handling error")
            display("StoreId handling error")
        }

        StoreIdLocalPartAbsoluteError(pb: PathBuf) {
            description("StoreId 'id' part is absolute (starts with '/') which is not allowed")
            display("StoreId 'id' part is absolute (starts with '/') which is not allowed: {:?}", pb)
        }

        StoreIdBuildFromFullPathError {
            description("Building StoreId from full file path failed")
            display("Building StoreId from full file path failed")
        }

        StoreIdHasNoBaseError(pb: PathBuf) {
            description("StoreId has no 'base' part")
            display("StoreId has no 'base' part: {:?}", pb)
        }

        CreateCallError(sid: StoreId) {
            description("Error when calling create()")
            display("Error when calling create({:?})", sid)
        }

        RetrieveCallError(sid: StoreId) {
            description("Error when calling retrieve()")
            display("Error when calling retrieve({:?})", sid)
        }

        GetCallError(sid: StoreId) {
            description("Error when calling get()")
            display("Error when calling get({:?})", sid)
        }

        UpdateCallError(sid: StoreId) {
            description("Error when calling update()")
            display("Error when calling update({:?})", sid)
        }

        RetrieveCopyCallError(sid: StoreId) {
            description("Error when calling retrieve_copy()")
            display("Error when calling retrieve_copy({:?})", sid)
        }

        DeleteCallError(sid: StoreId) {
            description("Error when calling delete()")
            display("Error when calling delete({:?})", sid)
        }

        MoveCallError(old: StoreId, new: StoreId) {
            description("Error when calling move()")
            display("Error when calling move({:?} -> {:?})", old, new)
        }

        // Parser-related errors

        MissingMainSection  {
            description("Missing main section")
            display("Missing main section")
        }

        MissingVersionInfo  {
            description("Missing version information in main section")
            display("Missing version information in main section")
        }

        NonTableInBaseTable {
            description("A non-table was found in the base table")
            display("A non-table was found in the base table")
        }

        HeaderInconsistency {
            description("The header is inconsistent")
            display("The header is inconsistent")
        }
    }
}


M lib/core/libimagstore/src/file_abstraction/fs.rs => lib/core/libimagstore/src/file_abstraction/fs.rs +59 -31
@@ 22,8 22,7 @@ use std::io::{Seek, SeekFrom, Read};
use std::path::{Path, PathBuf};
use std::sync::Arc;

use error::{StoreError as SE, StoreErrorKind as SEK};
use error::ResultExt;
use libimagerror::errors::ErrorMsg as EM;

use super::FileAbstraction;
use super::FileAbstractionInstance;


@@ 34,6 33,9 @@ use file_abstraction::iter::PathIterator;
use file_abstraction::iter::PathIterBuilder;

use walkdir::WalkDir;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;

#[derive(Debug)]
pub struct FSFileAbstractionInstance(PathBuf);


@@ 43,34 45,41 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
    /**
     * Get the content behind this file
     */
    fn get_file_content(&mut self, id: StoreId) -> Result<Entry, SE> {
    fn get_file_content(&mut self, id: StoreId) -> Result<Option<Entry>> {
        debug!("Getting lazy file: {:?}", self);

        let mut file = open_file(&self.0)
            .chain_err(|| SEK::FileNotFound)?;
        let mut file = match open_file(&self.0) {
            Err(err)       => return Err(Error::from(err)),
            Ok(None)       => return Ok(None),
            Ok(Some(file)) => file,
        };

        file.seek(SeekFrom::Start(0)).chain_err(|| SEK::FileNotSeeked)?;
        file.seek(SeekFrom::Start(0)).context(EM::FileNotSeeked)?;

        let mut s = String::new();

        file.read_to_string(&mut s)
            .chain_err(|| SEK::IoError)
            .context(EM::IO)
            .map_err(Error::from)
            .map(|_| s)
            .and_then(|s| Entry::from_str(id, &s))
            .and_then(|s: String| Entry::from_str(id, &s))
            .map(Some)
    }

    /**
     * Write the content of this file
     */
    fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE> {
    fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
        use std::io::Write;

        let buf      = buf.to_str()?.into_bytes();
        let mut file = create_file(&self.0).chain_err(|| SEK::FileNotCreated)?;
        let mut file = create_file(&self.0).context(EM::FileNotCreated)?;

        file.seek(SeekFrom::Start(0)).chain_err(|| SEK::FileNotCreated)?;
        file.set_len(buf.len() as u64).chain_err(|| SEK::FileNotWritten)?;
        file.write_all(&buf).chain_err(|| SEK::FileNotWritten)
        file.seek(SeekFrom::Start(0)).context(EM::FileNotCreated)?;
        file.set_len(buf.len() as u64).context(EM::FileNotWritten)?;
        file.write_all(&buf)
            .context(EM::FileNotWritten)
            .map_err(Error::from)
    }
}



@@ 82,19 91,24 @@ pub struct FSFileAbstraction {}

impl FileAbstraction for FSFileAbstraction {

    fn remove_file(&self, path: &PathBuf) -> Result<(), SE> {
        remove_file(path).chain_err(|| SEK::FileNotRemoved)
    fn remove_file(&self, path: &PathBuf) -> Result<()> {
        remove_file(path)
            .context(EM::FileNotRemoved)
            .map_err(Error::from)
    }

    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
        copy(from, to).chain_err(|| SEK::FileNotCopied).map(|_| ())
    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
        copy(from, to)
            .map(|_| ())
            .context(EM::FileNotCopied)
            .map_err(Error::from)
    }

    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
        match to.parent() {
            Some(p) => if !p.exists() {
                debug!("Creating: {:?}", p);
                let _ = create_dir_all(&PathBuf::from(p))?;
                let _ = create_dir_all(&PathBuf::from(p)).context(EM::DirNotCreated)?;
            },
            None => {
                debug!("Failed to find parent. This looks like it will fail now");


@@ 103,19 117,23 @@ impl FileAbstraction for FSFileAbstraction {
        }

        debug!("Renaming {:?} to {:?}", from, to);
        rename(from, to).chain_err(|| SEK::FileNotRenamed)
        rename(from, to)
            .context(EM::FileNotRenamed)
            .map_err(Error::from)
    }

    fn create_dir_all(&self, path: &PathBuf) -> Result<(), SE> {
    fn create_dir_all(&self, path: &PathBuf) -> Result<()> {
        debug!("Creating: {:?}", path);
        create_dir_all(path).chain_err(|| SEK::DirNotCreated)
        create_dir_all(path)
            .context(EM::DirNotCreated)
            .map_err(Error::from)
    }

    fn exists(&self, path: &PathBuf) -> Result<bool, SE> {
    fn exists(&self, path: &PathBuf) -> Result<bool> {
        Ok(path.exists())
    }

    fn is_file(&self, path: &PathBuf) -> Result<bool, SE> {
    fn is_file(&self, path: &PathBuf) -> Result<bool> {
        Ok(path.is_file())
    }



@@ 124,13 142,13 @@ impl FileAbstraction for FSFileAbstraction {
    }

    /// We return nothing from the FS here.
    fn drain(&self) -> Result<Drain, SE> {
    fn drain(&self) -> Result<Drain> {
        Ok(Drain::empty())
    }

    /// FileAbstraction::fill implementation that consumes the Drain and writes everything to the
    /// filesystem
    fn fill(&mut self, mut d: Drain) -> Result<(), SE> {
    fn fill(&mut self, mut d: Drain) -> Result<()> {
        d.iter()
            .fold(Ok(()), |acc, (path, element)| {
                acc.and_then(|_| self.new_instance(path).write_file_content(&element))


@@ 141,7 159,7 @@ impl FileAbstraction for FSFileAbstraction {
                          basepath: PathBuf,
                          storepath: PathBuf,
                          backend: Arc<FileAbstraction>)
        -> Result<PathIterator, SE>
        -> Result<PathIterator>
    {
        trace!("Building PathIterator object");
        Ok(PathIterator::new(Box::new(WalkDirPathIterBuilder { basepath }), storepath, backend))


@@ 153,13 171,15 @@ pub(crate) struct WalkDirPathIterBuilder {
}

impl PathIterBuilder for WalkDirPathIterBuilder {
    fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf, SE>>> {
    fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf>>> {
        Box::new(WalkDir::new(self.basepath.clone())
            .min_depth(1)
            .max_open(100)
            .into_iter()
            .map(|r| {
                r.map(|e| PathBuf::from(e.path())).chain_err(|| SE::from_kind(SEK::FileError))
                r.map(|e| PathBuf::from(e.path()))
                    .context(format_err!("Error in Walkdir"))
                    .map_err(Error::from)
            }))
    }



@@ 168,8 188,16 @@ impl PathIterBuilder for WalkDirPathIterBuilder {
    }
}

fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {
    OpenOptions::new().write(true).read(true).open(p)
fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<Option<File>> {
    match OpenOptions::new().write(true).read(true).open(p) {
        Err(e) => {
            match e.kind() {
                ::std::io::ErrorKind::NotFound => return Ok(None),
                _ => return Err(e),
            }
        },
        Ok(file) => Ok(Some(file))
    }
}

fn create_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {

M lib/core/libimagstore/src/file_abstraction/inmemory.rs => lib/core/libimagstore/src/file_abstraction/inmemory.rs +31 -26
@@ 18,15 18,17 @@
//

use std::path::PathBuf;

use error::StoreError as SE;
use error::StoreErrorKind as SEK;
use std::collections::HashMap;
use std::sync::Mutex;
use std::cell::RefCell;
use std::sync::Arc;
use std::ops::Deref;

use libimagerror::errors::ErrorMsg as EM;

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

use super::FileAbstraction;
use super::FileAbstractionInstance;
use super::Drain;


@@ 62,21 64,21 @@ impl FileAbstractionInstance for InMemoryFileAbstractionInstance {
    /**
     * Get the mutable file behind a InMemoryFileAbstraction object
     */
    fn get_file_content(&mut self, _: StoreId) -> Result<Entry, SE> {
    fn get_file_content(&mut self, _: StoreId) -> Result<Option<Entry>> {
        debug!("Getting lazy file: {:?}", self);

        self.fs_abstraction
            .lock()
            .map_err(|_| SE::from_kind(SEK::LockError))
            .and_then(|mut mtx| {
            .map_err(|_| Error::from(EM::LockError))
            .map(|mut mtx| {
                mtx.get_mut()
                    .get(&self.absent_path)
                    .cloned()
                    .ok_or_else(|| SE::from_kind(SEK::FileNotFound))
            })
            .map_err(Error::from)
    }

    fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE> {
    fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
        match *self {
            InMemoryFileAbstractionInstance { ref absent_path, .. } => {
                let mut mtx = self.fs_abstraction.lock().expect("Locking Mutex failed");


@@ 99,18 101,19 @@ impl InMemoryFileAbstraction {
        &self.virtual_filesystem
    }

    fn backend_cloned<'a>(&'a self) -> Result<HashMap<PathBuf, Entry>, SE> {
    fn backend_cloned<'a>(&'a self) -> Result<HashMap<PathBuf, Entry>> {
        self.virtual_filesystem
            .lock()
            .map_err(|_| SE::from_kind(SEK::LockError))
            .map_err(|_| Error::from(EM::LockError))
            .map(|mtx| mtx.deref().borrow().clone())
            .into()
    }

}

impl FileAbstraction for InMemoryFileAbstraction {

    fn remove_file(&self, path: &PathBuf) -> Result<(), SE> {
    fn remove_file(&self, path: &PathBuf) -> Result<()> {
        debug!("Removing: {:?}", path);
        self.backend()
            .lock()


@@ 118,43 121,43 @@ impl FileAbstraction for InMemoryFileAbstraction {
            .get_mut()
            .remove(path)
            .map(|_| ())
            .ok_or_else(|| SE::from_kind(SEK::FileNotFound))
            .ok_or_else(|| EM::FileNotFound.into())
    }

    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
        debug!("Copying : {:?} -> {:?}", from, to);
        let mut mtx = self.backend().lock().expect("Locking Mutex failed");
        let backend = mtx.get_mut();

        let a = backend.get(from).cloned().ok_or_else(|| SE::from_kind(SEK::FileNotFound))?;
        let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?;
        backend.insert(to.clone(), a);
        debug!("Copying: {:?} -> {:?} worked", from, to);
        Ok(())
    }

    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
        debug!("Renaming: {:?} -> {:?}", from, to);
        let mut mtx = self.backend().lock().expect("Locking Mutex failed");
        let backend = mtx.get_mut();

        let a = backend.remove(from).ok_or_else(|| SE::from_kind(SEK::FileNotFound))?;
        let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?;
        backend.insert(to.clone(), a);
        debug!("Renaming: {:?} -> {:?} worked", from, to);
        Ok(())
    }

    fn create_dir_all(&self, _: &PathBuf) -> Result<(), SE> {
    fn create_dir_all(&self, _: &PathBuf) -> Result<()> {
        Ok(())
    }

    fn exists(&self, pb: &PathBuf) -> Result<bool, SE> {
    fn exists(&self, pb: &PathBuf) -> Result<bool> {
        let mut mtx = self.backend().lock().expect("Locking Mutex failed");
        let backend = mtx.get_mut();

        Ok(backend.contains_key(pb))
    }

    fn is_file(&self, pb: &PathBuf) -> Result<bool, SE> {
    fn is_file(&self, pb: &PathBuf) -> Result<bool> {
        // Because we only store Entries in the memory-internal backend, we only have to check for
        // existance here, as if a path exists in the inmemory storage, it is always mapped to an
        // entry. hence it is always a path to a file


@@ 165,13 168,15 @@ impl FileAbstraction for InMemoryFileAbstraction {
        Box::new(InMemoryFileAbstractionInstance::new(self.backend().clone(), p))
    }

    fn drain(&self) -> Result<Drain, SE> {
    fn drain(&self) -> Result<Drain> {
        self.backend_cloned().map(Drain::new)
    }

    fn fill<'a>(&'a mut self, mut d: Drain) -> Result<(), SE> {
    fn fill<'a>(&'a mut self, mut d: Drain) -> Result<()> {
        debug!("Draining into : {:?}", self);
        let mut mtx = self.backend().lock().map_err(|_| SEK::LockError)?;
        let mut mtx = self.backend()
            .lock()
            .map_err(|_| EM::LockError)?;
        let backend = mtx.get_mut();

        for (path, element) in d.iter() {


@@ 182,17 187,17 @@ impl FileAbstraction for InMemoryFileAbstraction {
        Ok(())
    }

    fn pathes_recursively(&self, _basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator, SE> {
    fn pathes_recursively(&self, _basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator> {
        trace!("Building PathIterator object (inmemory implementation)");
        let keys : Vec<PathBuf> = self
            .backend()
            .lock()
            .map_err(|_| SE::from_kind(SEK::FileError))?
            .map_err(|_| EM::LockError)?
            .get_mut()
            .keys()
            .map(PathBuf::from)
            .map(Ok)
            .collect::<Result<_, SE>>()?; // we have to collect() because of the lock() above.
            .collect::<Result<_>>()?; // we have to collect() because of the lock() above.

        Ok(PathIterator::new(Box::new(InMemPathIterBuilder(keys)), storepath, backend))
    }


@@ 201,7 206,7 @@ impl FileAbstraction for InMemoryFileAbstraction {
pub(crate) struct InMemPathIterBuilder(Vec<PathBuf>);

impl PathIterBuilder for InMemPathIterBuilder {
    fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf, SE>>> {
    fn build_iter(&self) -> Box<Iterator<Item = Result<PathBuf>>> {
        Box::new(self.0.clone().into_iter().map(Ok))
    }


M lib/core/libimagstore/src/file_abstraction/iter.rs => lib/core/libimagstore/src/file_abstraction/iter.rs +2 -1
@@ 20,7 20,8 @@
use std::path::PathBuf;
use std::sync::Arc;

use error::Result;
use failure::Fallible as Result;

use storeid::StoreId;
use file_abstraction::FileAbstraction;


M lib/core/libimagstore/src/file_abstraction/mod.rs => lib/core/libimagstore/src/file_abstraction/mod.rs +16 -15
@@ 22,7 22,8 @@ use std::fmt::Debug;
use std::collections::HashMap;
use std::sync::Arc;

use error::StoreError as SE;
use failure::Fallible as Result;

use store::Entry;
use storeid::StoreId;



@@ 38,20 39,20 @@ use self::iter::PathIterator;

/// An abstraction trait over filesystem actions
pub trait FileAbstraction : Debug {
    fn remove_file(&self, path: &PathBuf) -> Result<(), SE>;
    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE>;
    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE>;
    fn create_dir_all(&self, _: &PathBuf) -> Result<(), SE>;
    fn remove_file(&self, path: &PathBuf) -> Result<()>;
    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()>;
    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()>;
    fn create_dir_all(&self, _: &PathBuf) -> Result<()>;

    fn exists(&self, &PathBuf) -> Result<bool, SE>;
    fn is_file(&self, &PathBuf) -> Result<bool, SE>;
    fn exists(&self, &PathBuf) -> Result<bool>;
    fn is_file(&self, &PathBuf) -> Result<bool>;

    fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance>;

    fn drain(&self) -> Result<Drain, SE>;
    fn fill<'a>(&'a mut self, d: Drain) -> Result<(), SE>;
    fn drain(&self) -> Result<Drain>;
    fn fill<'a>(&'a mut self, d: Drain) -> Result<()>;

    fn pathes_recursively(&self, basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator, SE>;
    fn pathes_recursively(&self, basepath: PathBuf, storepath: PathBuf, backend: Arc<FileAbstraction>) -> Result<PathIterator>;
}

/// An abstraction trait over actions on files


@@ 61,8 62,8 @@ pub trait FileAbstractionInstance : Debug {
    ///
    /// The `StoreId` is passed because the backend does not know where the Entry lives, but the
    /// Entry type itself must be constructed with the id.
    fn get_file_content(&mut self, id: StoreId) -> Result<Entry, SE>;
    fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE>;
    fn get_file_content(&mut self, id: StoreId) -> Result<Option<Entry>>;
    fn write_file_content(&mut self, buf: &Entry) -> Result<()>;
}

pub struct Drain(HashMap<PathBuf, Entry>);


@@ 119,7 120,7 @@ version = "{}"
Hello World"#, env!("CARGO_PKG_VERSION"))).unwrap();

        lf.write_file_content(&file).unwrap();
        let bah = lf.get_file_content(loca).unwrap();
        let bah = lf.get_file_content(loca).unwrap().unwrap();
        assert_eq!(bah.get_content(), "Hello World");
    }



@@ 140,7 141,7 @@ Hello World
baz"#, env!("CARGO_PKG_VERSION"))).unwrap();

        lf.write_file_content(&file).unwrap();
        let bah = lf.get_file_content(loca).unwrap();
        let bah = lf.get_file_content(loca).unwrap().unwrap();
        assert_eq!(bah.get_content(), "Hello World\nbaz");
    }



@@ 163,7 164,7 @@ baz
"#, env!("CARGO_PKG_VERSION"))).unwrap();

        lf.write_file_content(&file).unwrap();
        let bah = lf.get_file_content(loca).unwrap();
        let bah = lf.get_file_content(loca).unwrap().unwrap();
        assert_eq!(bah.get_content(), "Hello World\nbaz\n\n");
    }


M lib/core/libimagstore/src/iter.rs => lib/core/libimagstore/src/iter.rs +16 -24
@@ 31,41 31,34 @@ macro_rules! mk_iterator_mod {
            #[allow(unused_imports)]
            use store::FileLockEntry;
            use store::Store;
            use error::StoreError;
            use std::result::Result as RResult;
            use failure::Fallible as Result;

            pub struct $itername<'a, E>(Box<Iterator<Item = RResult<StoreId, E>> + 'a>, &'a Store)
                where E: From<StoreError>;
            pub struct $itername<'a>(Box<Iterator<Item = Result<StoreId>> + 'a>, &'a Store);

            impl<'a, E> $itername<'a, E>
                where E: From<StoreError>
            impl<'a> $itername<'a>
            {
                pub fn new(inner: Box<Iterator<Item = RResult<StoreId, E>> + 'a>, store: &'a Store) -> Self {
                pub fn new(inner: Box<Iterator<Item = Result<StoreId>> + 'a>, store: &'a Store) -> Self {
                    $itername(inner, store)
                }
            }

            impl<'a, E> Iterator for $itername<'a, E>
                where E: From<StoreError>
            impl<'a> Iterator for $itername<'a>
            {
                type Item = RResult<$yield, E>;
                type Item = Result<$yield>;

                fn next(&mut self) -> Option<Self::Item> {
                    self.0.next().map(|id| $fun(id?, self.1).map_err(E::from))
                    self.0.next().map(|id| $fun(id?, self.1))
                }
            }

            pub trait $extname<'a, E>
                where E: From<StoreError>
            {
                fn $extfnname(self, store: &'a Store) -> $itername<'a, E>;
            pub trait $extname<'a> {
                fn $extfnname(self, store: &'a Store) -> $itername<'a>;
            }

            impl<'a, I, E> $extname<'a, E> for I
                where I: Iterator<Item = RResult<StoreId, E>> + 'a,
                      E: From<StoreError>
            impl<'a, I> $extname<'a> for I
                where I: Iterator<Item = Result<StoreId>> + 'a
            {
                fn $extfnname(self, store: &'a Store) -> $itername<'a, E> {
                fn $extfnname(self, store: &'a Store) -> $itername<'a> {
                    $itername(Box::new(self), store)
                }
            }


@@ 151,8 144,7 @@ use self::get::StoreGetIterator;
use self::retrieve::StoreRetrieveIterator;
use file_abstraction::iter::PathIterator;
use store::Store;
use error::StoreError;
use error::Result;
use failure::Fallible as Result;

/// Iterator for iterating over all (or a subset of all) entries
///


@@ 194,21 186,21 @@ impl<'a> Entries<'a> {
    /// Transform the iterator into a StoreDeleteIterator
    ///
    /// This immitates the API from `libimagstore::iter`.
    pub fn into_delete_iter(self) -> StoreDeleteIterator<'a, StoreError> {
    pub fn into_delete_iter(self) -> StoreDeleteIterator<'a> {
        StoreDeleteIterator::new(Box::new(self.0), self.1)
    }

    /// Transform the iterator into a StoreGetIterator
    ///
    /// This immitates the API from `libimagstore::iter`.
    pub fn into_get_iter(self) -> StoreGetIterator<'a, StoreError> {
    pub fn into_get_iter(self) -> StoreGetIterator<'a> {
        StoreGetIterator::new(Box::new(self.0), self.1)
    }

    /// Transform the iterator into a StoreRetrieveIterator
    ///
    /// This immitates the API from `libimagstore::iter`.
    pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a, StoreError> {
    pub fn into_retrieve_iter(self) -> StoreRetrieveIterator<'a> {
        StoreRetrieveIterator::new(Box::new(self.0), self.1)
    }


M lib/core/libimagstore/src/lib.rs => lib/core/libimagstore/src/lib.rs +1 -2
@@ 44,7 44,7 @@ extern crate semver;
extern crate walkdir;
#[macro_use] extern crate is_match;
extern crate serde_json;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate failure;
extern crate toml_query;

extern crate libimagerror;


@@ 53,7 53,6 @@ extern crate libimagutil;
#[macro_use] mod util;

pub mod storeid;
pub mod error;
pub mod iter;
pub mod store;
mod configuration;

M lib/core/libimagstore/src/store.rs => lib/core/libimagstore/src/store.rs +106 -123
@@ 31,12 31,16 @@ use std::fmt::Formatter;
use std::fmt::Debug;
use std::fmt::Error as FMTError;

use libimagerror::errors::ErrorMsg as EM;

use toml::Value;
use toml_query::read::TomlValueReadExt;
use toml_query::read::TomlValueReadTypeExt;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;
use failure::Error;

use error::{StoreError as SE, StoreErrorKind as SEK};
use error::ResultExt;
use storeid::{IntoStoreId, StoreId};
use iter::Entries;
use file_abstraction::FileAbstractionInstance;


@@ 48,9 52,6 @@ pub use file_abstraction::InMemoryFileAbstraction;

use libimagutil::debug_result::*;

/// The Result Type returned by any interaction with the store that could fail
pub type Result<T> = RResult<T, SE>;


#[derive(Debug, PartialEq)]
enum StoreEntryStatus {


@@ 76,7 77,7 @@ impl StoreEntry {
        {
            open_file(pb.clone())
                .and_then(|f| f.lock_exclusive())
                .chain_err(|| SEK::IoError)?;
                .with_context(|| EM::IO)?;
        }

        Ok(StoreEntry {


@@ 94,15 95,12 @@ impl StoreEntry {

    fn get_entry(&mut self) -> Result<Entry> {
        if !self.is_borrowed() {
            self.file
                .get_file_content(self.id.clone())
                .or_else(|err| if is_match!(err.kind(), &SEK::FileNotFound) {
                    Ok(Entry::new(self.id.clone()))
                } else {
                    Err(err)
                })
            match self.file.get_file_content(self.id.clone())? {
                Some(file) => Ok(file),
                None       => Ok(Entry::new(self.id.clone()))
            }
        } else {
            Err(SE::from_kind(SEK::EntryAlreadyBorrowed(self.id.clone())))
            Err(format_err!("EntryAlreadyBorrowed: {}", self.id))
        }
    }



@@ 184,18 182,19 @@ impl Store {
        debug!("Building new Store object");
        if !location.exists() {
            if !config_implicit_store_create_allowed(store_config)? {
                return Err(SE::from_kind(SEK::CreateStoreDirDenied))
                    .chain_err(|| SEK::FileError)
                    .chain_err(|| SEK::IoError);
                return Err(format_err!("CreateStoreDirDenied"))
                    .context(EM::FileError)
                    .context(EM::IO)
                    .map_err(Error::from)
            }

            backend
                .create_dir_all(&location)
                .chain_err(|| SEK::StorePathCreate(location.clone()))
                .context(format_err!("StorePathCreate: {}", location.display()))
                .map_dbg_err_str("Failed")?;
        } else if location.is_file() {
            debug!("Store path exists as file");
            return Err(SE::from_kind(SEK::StorePathExists(location)));
            return Err(format_err!("StorePathExists: {}", location.display()));
        }

        let store = Store {


@@ 218,45 217,34 @@ impl Store {
    ///
    /// On success: FileLockEntry
    ///
    /// On error:
    ///  - Errors StoreId::into_storeid() might return
    ///  - EntryAlreadyExists(id) if the entry exists
    ///  - CreateCallError(LockPoisoned()) if the internal lock is poisened.
    ///  - CreateCallError(EntryAlreadyExists()) if the entry exists already.
    ///
    pub fn create<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
        let id = id.into_storeid()?.with_base(self.path().clone());

        debug!("Creating id: '{}'", id);

        let exists = self.entries
        let exists = id.exists()? || self.entries
            .read()
            .map_err(|_| SE::from_kind(SEK::LockPoisoned))
            .map(|map| map.contains_key(&id))
            .and_then(|exists| if exists {
                Ok(exists)
            } else {
                let pb = id.clone().into_pathbuf().map_err(SE::from)?;
                self.backend.exists(&pb)
            })
            .chain_err(|| SEK::CreateCallError(id.clone()))?;
            .map_err(|_| Error::from(EM::LockError))
            .context(format_err!("CreateCallError: {}", id))?;

        if exists {
            debug!("Entry exists: {:?}", id);
            return Err(SEK::EntryAlreadyExists(id).into());
            return Err(format_err!("EntryAlreadyExists: {}", id));
        }

        {
            let mut hsmap = self
                .entries
                .write()
                .map_err(|_| SE::from_kind(SEK::LockPoisoned))
                .chain_err(|| SEK::CreateCallError(id.clone()))?;
                .map_err(|_| Error::from(EM::LockError))
                .context(format_err!("CreateCallError: {}", id))?;

            if hsmap.contains_key(&id) {
                debug!("Cannot create, internal cache already contains: '{}'", id);
                return Err(SE::from_kind(SEK::EntryAlreadyExists(id.clone())))
                           .chain_err(|| SEK::CreateCallError(id.clone()));
                return Err(format_err!("EntryAlreadyExists: {}", id))
                           .context(format_err!("CreateCallError: {}", id))
                           .map_err(Error::from)
            }
            hsmap.insert(id.clone(), {
                debug!("Creating: '{}'", id);


@@ 281,17 269,13 @@ impl Store {
    ///
    /// On success: FileLockEntry
    ///
    /// On error:
    ///  - Errors StoreId::into_storeid() might return
    ///  - RetrieveCallError(LockPoisoned()) if the internal lock is poisened.
    ///
    pub fn retrieve<'a, S: IntoStoreId>(&'a self, id: S) -> Result<FileLockEntry<'a>> {
        let id = id.into_storeid()?.with_base(self.path().clone());
        debug!("Retrieving id: '{}'", id);
        let entry = self
            .entries
            .write()
            .map_err(|_| SE::from_kind(SEK::LockPoisoned))
            .map_err(|_| Error::from(EM::LockError))
            .and_then(|mut es| {
                let new_se = StoreEntry::new(id.clone(), &self.backend)?;
                let se = es.entry(id.clone()).or_insert(new_se);


@@ 299,7 283,7 @@ impl Store {
                se.status = StoreEntryStatus::Borrowed;
                entry
            })
            .chain_err(|| SEK::RetrieveCallError(id.clone()))?;
            .context(format_err!("RetrieveCallError: {}", id))?;

        debug!("Constructing FileLockEntry: '{}'", id);
        Ok(FileLockEntry::new(self, entry))


@@ 320,24 304,21 @@ impl Store {

        debug!("Getting id: '{}'", id);

        let exists = self.entries
        let exists = id.exists()? || self.entries
            .read()
            .map_err(|_| SE::from_kind(SEK::LockPoisoned))
            .map(|map| map.contains_key(&id))
            .and_then(|exists| if exists {
                Ok(exists)
            } else {
                let pb = id.clone().into_pathbuf().map_err(SE::from)?;
                self.backend.exists(&pb)
            })
            .chain_err(|| SEK::GetCallError(id.clone()))?;
            .map_err(|_| Error::from(EM::LockError))
            .context(format_err!("GetCallError: {}", id))?;

        if !exists {
            debug!("Does not exist in internal cache or filesystem: {:?}", id);
            return Ok(None);
        }

        self.retrieve(id.clone()).map(Some).chain_err(|| SEK::GetCallError(id))
        self.retrieve(id.clone())
            .map(Some)
            .context(format_err!("GetCallError: {}", id))
            .map_err(Error::from)
    }

    /// Write (update) the `FileLockEntry` to disk


@@ 346,15 327,11 @@ impl Store {
    ///
    /// On success: Entry
    ///
    /// On error:
    ///  - UpdateCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
    ///  - IdNotFound() if the entry was not found in the stor
    ///  - Errors Entry::verify() might return
    ///  - Errors StoreEntry::write_entry() might return
    ///
    pub fn update<'a>(&'a self, entry: &mut FileLockEntry<'a>) -> Result<()> {
        debug!("Updating FileLockEntry at '{}'", entry.get_location());
        self._update(entry, false).chain_err(|| SEK::UpdateCallError(entry.get_location().clone()))
        self._update(entry, false)
            .context(format_err!("UpdateCallError: {}", entry.get_location()))
            .map_err(Error::from)
    }

    /// Internal method to write to the filesystem store.


@@ 365,10 342,11 @@ impl Store {
    /// it is not public.
    ///
    fn _update<'a>(&'a self, entry: &mut FileLockEntry<'a>, modify_presence: bool) -> Result<()> {
        let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
        let mut hsmap = self.entries.write()
            .map_err(|_| Error::from(EM::LockError))?;

        let se = hsmap.get_mut(&entry.location).ok_or_else(|| {
            SE::from_kind(SEK::IdNotFound(entry.location.clone()))
            EM::EntryNotFound(entry.location.local_display_string())
        })?;

        assert!(se.is_borrowed(), "Tried to update a non borrowed entry.");


@@ 401,7 379,8 @@ impl Store {
    pub fn flush_cache(&self) -> Result<()> {
        // We borrow this early so that between the aggregation of the flushables and the actual
        // flush, there is no borrowing from the store.
        let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
        let mut hsmap = self.entries.write()
            .map_err(|_| Error::from(EM::LockError))?;
        let mut to_flush = vec![];

        for (storeid, se) in hsmap.deref() {


@@ 421,37 400,34 @@ impl Store {

    /// The number of elements in the internal cache
    pub fn cache_size(&self) -> Result<usize> {
        let hsmap = self.entries.read().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
        let hsmap = self.entries.read().map_err(|_| Error::from(EM::LockError))?;
        Ok(hsmap.iter().count())
    }

    /// The size of the internal cache
    pub fn cache_capacity(&self) -> Result<usize> {
        let hsmap = self.entries.read().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
        let hsmap = self.entries.read().map_err(|_| Error::from(EM::LockError))?;
        Ok(hsmap.capacity())
    }

    /// Get a copy of a given entry, this cannot be used to mutate the one on disk
    // Get a copy of a given entry, this cannot be used to mutate the one on disk
    ///
    /// # Return value
    ///
    /// On success: Entry
    ///
    /// On error:
    ///  - RetrieveCopyCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
    ///  - RetrieveCopyCallError(IdLocked()) if the Entry is borrowed currently
    ///  - Errors StoreEntry::new() might return
    ///
    pub fn get_copy<S: IntoStoreId>(&self, id: S) -> Result<Entry> {
        let id = id.into_storeid()?.with_base(self.path().clone());
        debug!("Retrieving copy of '{}'", id);
        let entries = self.entries.write()
            .map_err(|_| SE::from_kind(SEK::LockPoisoned))
            .chain_err(|| SEK::RetrieveCopyCallError(id.clone()))?;
            .map_err(|_| Error::from(EM::LockError))
            .context(format_err!("RetrieveCopyCallError: {}", id))?;

        // if the entry is currently modified by the user, we cannot drop it
        if entries.get(&id).map(|e| e.is_borrowed()).unwrap_or(false) {
            return Err(SE::from_kind(SEK::IdLocked)).chain_err(|| SEK::RetrieveCopyCallError(id));
            return Err(EM::IdLocked)
                .context(format_err!("RetrieveCopyCallError: {}", id))
                .map_err(Error::from)
        }

        StoreEntry::new(id, &self.backend)?.get_entry()


@@ 463,27 439,29 @@ impl Store {
    ///
    /// On success: ()
    ///
    /// On error:
    ///  - DeleteCallError(LockPoisoned()) if the internal write lock cannot be aquierd.
    ///  - DeleteCallError(FileNotFound()) if the StoreId refers to a non-existing entry.
    ///  - DeleteCallError(FileError()) if the internals failed to remove the file.
    ///
    pub fn delete<S: IntoStoreId>(&self, id: S) -> Result<()> {
        let id = id.into_storeid()?.with_base(self.path().clone());

        debug!("Deleting id: '{}'", id);

        // Small optimization: We need the pathbuf for deleting, but when calling
        // StoreId::exists(), a PathBuf object gets allocated. So we simply get a
        // PathBuf here, check whether it is there and if it is, we can re-use it to
        // delete the filesystem file.
        let pb = id.clone().into_pathbuf()?;

        {
            let mut entries = self
                .entries
                .write()
                .map_err(|_| SE::from_kind(SEK::LockPoisoned))
                .chain_err(|| SEK::DeleteCallError(id.clone()))?;
                .map_err(|_| Error::from(EM::LockError))
                .context(format_err!("DeleteCallError: {}", id))?;

            let do_remove = match entries.get(&id) {
                Some(e) => if e.is_borrowed() { // entry is currently borrowed, we cannot delete it
                    return Err(SE::from_kind(SEK::IdLocked)).chain_err(|| SEK::DeleteCallError(id));
                    return Err(Error::from(EM::LockError))
                        .context(format_err!("DeleteCallError: {}", id))
                        .map_err(Error::from)
                    // false
                } else { // Entry is in the cache
                    // Remove Entry from the cache


@@ 496,8 474,9 @@ impl Store {

                    if !self.backend.exists(&pb)? {
                        debug!("Seems like {:?} is not even on the FS", pb);
                        return Err(SE::from_kind(SEK::FileNotFound))
                            .chain_err(|| SEK::DeleteCallError(id))
                        return Err(EM::FileNotFound)
                            .context(format_err!("DeleteCallError: {}", id))
                            .map_err(Error::from)
                    } // else { continue }

                    false


@@ 513,8 492,8 @@ impl Store {
        let _ = self
            .backend
            .remove_file(&pb)
            .chain_err(|| SEK::FileError)
            .chain_err(|| SEK::DeleteCallError(id))?;
            .context(EM::FileError)
            .context(format_err!("DeleteCallError: {}", id))?;

        debug!("Deleted");
        Ok(())


@@ 540,12 519,13 @@ impl Store {
        let hsmap = self
            .entries
            .write()
            .map_err(|_| SE::from_kind(SEK::LockPoisoned))
            .chain_err(|| SEK::MoveCallError(entry.get_location().clone(), new_id.clone()))?;
            .map_err(|_| Error::from(EM::LockError))
            .context(format_err!("MoveCallError: {} -> {}", entry.get_location(), new_id))?;

        if hsmap.contains_key(&new_id) {
            return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())))
                .chain_err(|| SEK::MoveCallError(entry.get_location().clone(), new_id.clone()))
            return Err(format_err!("Entry exists already: {}", new_id.clone()))
                .context(format_err!("MoveCallError: {} -> {}", entry.get_location(), new_id))
                .map_err(Error::from)
        }

        let old_id = entry.get_location().clone();


@@ 560,8 540,9 @@ impl Store {
            } else {
                Ok(())
            })
            .chain_err(|| SEK::FileError)
            .chain_err(|| SEK::MoveCallError(old_id, new_id))
            .context(EM::FileError)
            .context(format_err!("MoveCallError: {} -> {}", old_id, new_id))
            .map_err(Error::from)
    }

    /// Move an entry without loading


@@ 604,10 585,11 @@ impl Store {
        debug!("Moving '{}' to '{}'", old_id, new_id);

        {
            let mut hsmap = self.entries.write().map_err(|_| SE::from_kind(SEK::LockPoisoned))?;
            let mut hsmap = self.entries.write()
                .map_err(|_| Error::from(EM::LockError))?;

            if hsmap.contains_key(&new_id) {
                return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())));
                return Err(format_err!("Entry already exists: {}", new_id));
            }
            debug!("New id does not exist in cache");



@@ 615,7 597,7 @@ impl Store {
            // if we have one, but it is borrowed, we really should not rename it, as this might
            // lead to strange errors
            if hsmap.get(&old_id).map(|e| e.is_borrowed()).unwrap_or(false) {
                return Err(SE::from_kind(SEK::EntryAlreadyBorrowed(old_id.clone())));
                return Err(format_err!("Entry already borrowed: {}", old_id));
            }

            debug!("Old id is not yet borrowed");


@@ 624,14 606,18 @@ impl Store {
            let new_id_pb = new_id.clone().with_base(self.path().clone()).into_pathbuf()?;

            if self.backend.exists(&new_id_pb)? {
                return Err(SE::from_kind(SEK::EntryAlreadyExists(new_id.clone())));
                return Err(format_err!("Entry already exists: {}", new_id));
            }
            debug!("New entry does not yet exist on filesystem. Good.");

            let _ = self
                .backend
                .rename(&old_id_pb, &new_id_pb)
                .chain_err(|| SEK::EntryRenameError(old_id_pb, new_id_pb))?;
                .context({
                    let old = old_id_pb.display().to_string();
                    let new = new_id_pb.display().to_string();
                    format_err!("Rename error: {} -> {}", old, new)
                })?;

            debug!("Rename worked on filesystem");



@@ 792,7 778,7 @@ impl Entry {
    pub fn from_reader<S: IntoStoreId>(loc: S, file: &mut Read) -> Result<Entry> {
        let text = {
            let mut s = String::new();
            file.read_to_string(&mut s)?;
            file.read_to_string(&mut s).context(EM::IO)?;
            s
        };
        Self::from_str(loc, &text[..])


@@ 828,7 814,9 @@ impl Entry {
    /// disk).
    pub fn to_str(&self) -> Result<String> {
        Ok(format!("---\n{header}---\n{content}",
                   header  = ::toml::ser::to_string_pretty(&self.header)?,
                   header  = ::toml::ser::to_string_pretty(&self.header)
                       .map_err(Error::from)