~matthiasbeyer/imag

85f8082cbee0e9ed65d1fb2b06d4c99dc6f6ce2c — Matthias Beyer 1 year, 11 months ago c7f5420 + d034a6f
Merge branch 'error-context' into master
39 files changed, 177 insertions(+), 65 deletions(-)

M lib/core/libimagrt/src/logger.rs
M lib/core/libimagrt/src/runtime.rs
M lib/core/libimagstore/src/configuration.rs
M lib/core/libimagstore/src/storeid.rs
M lib/domain/libimagbookmark/src/collection.rs
M lib/domain/libimagcontact/src/iter.rs
M lib/domain/libimagcontact/src/store.rs
M lib/domain/libimagdiary/src/diary.rs
M lib/domain/libimagdiary/src/diaryid.rs
M lib/domain/libimaghabit/src/habit.rs
M lib/domain/libimaghabit/src/iter.rs
M lib/domain/libimaghabit/src/util.rs
M lib/domain/libimaglog/src/log.rs
M lib/domain/libimagmail/src/mail.rs
M lib/domain/libimagmail/src/util.rs
M lib/domain/libimagnotes/src/iter.rs
M lib/domain/libimagnotes/src/note.rs
M lib/domain/libimagtimetrack/src/iter/create.rs
M lib/domain/libimagtimetrack/src/timetracking.rs
M lib/domain/libimagtimetrack/src/timetrackingstore.rs
M lib/domain/libimagwiki/src/wiki.rs
M lib/entry/libimagentrycategory/src/category.rs
M lib/entry/libimagentrycategory/src/entry.rs
M lib/entry/libimagentrycategory/src/iter.rs
M lib/entry/libimagentrydatetime/src/datetime.rs
M lib/entry/libimagentrydatetime/src/lib.rs
M lib/entry/libimagentryedit/src/edit.rs
M lib/entry/libimagentryfilter/src/builtin/header/field_exists.rs
M lib/entry/libimagentryfilter/src/lib.rs
M lib/entry/libimagentrygps/src/entry.rs
M lib/entry/libimagentrygps/src/lib.rs
M lib/entry/libimagentrylink/src/external.rs
M lib/entry/libimagentrylink/src/internal.rs
M lib/entry/libimagentryref/src/reference.rs
M lib/entry/libimagentrytag/src/tagable.rs
M lib/entry/libimagentryutil/src/isa.rs
M lib/entry/libimagentryutil/src/lib.rs
M lib/entry/libimagentryview/src/builtin/editor.rs
M lib/etc/libimaginteraction/src/ask.rs
M lib/core/libimagrt/src/logger.rs => lib/core/libimagrt/src/logger.rs +2 -0
@@ 370,6 370,7 @@ fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
                for (module_name, v) in t {
                    let destinations = inner_try! {
                        v.read("destinations")
                            .context("Failed reading header 'destinations'")
                            .map_err(Error::from)
                            .context(EM::TomlQueryError)?
                            .map(|val| {


@@ 384,6 385,7 @@ fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)

                    let level = inner_try! {
                        v.read_string("level")
                            .context("Failed reading header 'level'")
                            .map_err(Error::from)
                            .context(EM::TomlQueryError)?
                            .map(|s| match_log_level_str(&s))

M lib/core/libimagrt/src/runtime.rs => lib/core/libimagrt/src/runtime.rs +1 -0
@@ 408,6 408,7 @@ impl<'a> Runtime<'a> {

            let mut buf = String::new();
            lock.read_to_string(&mut buf)
                .context("Failed to read stdin to buffer")
                .map_err(Error::from)
                .and_then(|_| {
                    trace!("Got IDs = {}", buf);

M lib/core/libimagstore/src/configuration.rs => lib/core/libimagstore/src/configuration.rs +1 -0
@@ 34,6 34,7 @@ pub fn config_implicit_store_create_allowed(config: &Option<Value>) -> Result<bo

    if let Some(ref t) = *config {
        t.read_bool(key)
            .context(format_err!("Error reading header '{}' in configuration", key))
            .map_err(Error::from)
            .context(EM::TomlQueryError)?
            .ok_or_else(|| format_err!("Config key missing: {}", key))

M lib/core/libimagstore/src/storeid.rs => lib/core/libimagstore/src/storeid.rs +3 -0
@@ 184,6 184,9 @@ impl<'a> StoreIdWithBase<'a> {
               store_part.display());
        let p = full_path
            .strip_prefix(store_part)
            .context(format_err!("Cannot strip prefix '{}' from path: '{}'",
                                 store_part.display(),
                                 full_path.display()))
            .map_err(Error::from)
            .context(err_msg("Error building Store Id from full path"))?;
        Ok(StoreIdWithBase(store_part, PathBuf::from(p)))

M lib/domain/libimagbookmark/src/collection.rs => lib/domain/libimagbookmark/src/collection.rs +13 -3
@@ 27,6 27,7 @@
use regex::Regex;

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

use libimagstore::store::Store;


@@ 52,19 53,28 @@ impl<'a> BookmarkCollectionStore<'a> for Store {

    fn new(&'a self, name: &str) -> Result<FileLockEntry<'a>> {
        crate::module_path::new_id(name)
            .and_then(|id| self.create(id).map_err(Error::from))
            .and_then(|id| self.create(id)
                      .context("Failed to create FileLockEntry")
                      .map_err(Error::from))
            .context("Failed to create Id for new Bookmark Collection")
            .map_err(Error::from)
    }

    fn get(&'a self, name: &str) -> Result<Option<FileLockEntry<'a>>> {
        crate::module_path::new_id(name)
            .and_then(|id| self.get(id).map_err(Error::from))
            .and_then(|id| self.get(id)
                      .context("Failed to get FileLockEntry")
                      .map_err(Error::from))
            .context("Failed to get Bookmark Collection")
            .map_err(Error::from)
    }

    fn delete(&'a self, name: &str) -> Result<()> {
        crate::module_path::new_id(name)
            .and_then(|id| self.delete(id).map_err(Error::from))
            .and_then(|id| self.delete(id)
                      .context("Failed to delete FileLockEntry")
                      .map_err(Error::from))
            .context("Failed to delete Bookmark Collection")
            .map_err(Error::from)
    }


M lib/domain/libimagcontact/src/iter.rs => lib/domain/libimagcontact/src/iter.rs +12 -2
@@ 25,6 25,7 @@ use libimagerror::errors::ErrorMsg as EM;
use crate::contact::Contact;
use failure::Fallible as Result;
use failure::Error;
use failure::ResultExt;

pub struct ContactIter<'a>(StoreIdIterator, &'a Store);



@@ 44,12 45,21 @@ impl<'a> Iterator for ContactIter<'a> {
        loop {
            match self.0.next() {
                None          => return None,
                Some(Err(e))  => return Some(Err(e).map_err(Error::from)),
                Some(Err(e))  => {
                    let e = Err(e)
                        .context("Found error while iterating over contacts")
                        .map_err(Error::from);

                    return Some(e)
                },
                Some(Ok(sid)) => match self.1.get(sid.clone()).map_err(From::from) {
                    Err(e)          => return Some(Err(e)),
                    Ok(None)        => return
                        Some(Err(Error::from(EM::EntryNotFound(sid.local_display_string())))),
                    Ok(Some(entry)) => match entry.is_contact().map_err(Error::from) {
                    Ok(Some(entry)) => match entry.is_contact()
                        .context("Cannot check whether entry is a contact")
                        .map_err(Error::from)
                    {
                        Ok(true)    => return Some(Ok(entry)),
                        Ok(false)   => continue,
                        Err(e)      => return Some(Err(e)),

M lib/domain/libimagcontact/src/store.rs => lib/domain/libimagcontact/src/store.rs +2 -1
@@ 27,6 27,7 @@ use toml_query::insert::TomlValueInsertExt;
use vobject::vcard::Vcard;
use failure::Error;
use failure::Fallible as Result;
use failure::ResultExt;

use libimagstore::storeid::StoreId;
use libimagstore::iter::Entries;


@@ 152,7 153,7 @@ impl<'a> ContactStore<'a> for Store {
///
/// That means calculating the StoreId and the Value from the vcard data
fn prepare_fetching_from_store(buf: &str) -> Result<(StoreId, Value)> {
    let vcard = Vcard::build(&buf).map_err(Error::from)?;
    let vcard = Vcard::build(&buf).context("Cannot parse Vcard").map_err(Error::from)?;
    debug!("Parsed: {:?}", vcard);

    let uid = vcard.uid()

M lib/domain/libimagdiary/src/diary.rs => lib/domain/libimagdiary/src/diary.rs +2 -0
@@ 30,6 30,7 @@ use itertools::Itertools;
use chrono::naive::NaiveDateTime;
use chrono::Timelike;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;

use crate::entry::IsDiaryEntry;


@@ 141,6 142,7 @@ impl Diary for Store {
    fn diary_names(&self) -> Result<DiaryNameIterator> {
        self.entries()
            .map(|it| DiaryNameIterator::new(it.into_storeid_iter()))
            .context("Failed building DiaryNameIteator from entries iterator")
            .map_err(Error::from)
    }


M lib/domain/libimagdiary/src/diaryid.rs => lib/domain/libimagdiary/src/diaryid.rs +3 -0
@@ 234,6 234,7 @@ impl FromStoreId for DiaryId {
        let day: Result<u32> = next_component(&mut cmps)
            .and_then(|s| {
                s.parse::<u32>()
                    .context("Failed to parse day from u32")
                    .map_err(Error::from)
                    .context(err_msg("ID parse error"))
                    .map_err(Error::from)


@@ 242,6 243,7 @@ impl FromStoreId for DiaryId {
        let month: Result<u32> = next_component(&mut cmps)
            .and_then(|s| {
                s.parse::<u32>()
                    .context("Failed to parse month from u32")
                    .map_err(Error::from)
                    .context(err_msg("ID Parse error"))
                    .map_err(Error::from)


@@ 250,6 252,7 @@ impl FromStoreId for DiaryId {
        let year: Result<i32> = next_component(&mut cmps)
            .and_then(|s| {
                s.parse::<i32>()
                    .context("Failed to parse year from i32")
                    .map_err(Error::from)
                    .context(err_msg("ID Parse error"))
                    .map_err(Error::from)

M lib/domain/libimaghabit/src/habit.rs => lib/domain/libimaghabit/src/habit.rs +4 -1
@@ 25,6 25,7 @@ use chrono::Local;
use chrono::NaiveDate;
use failure::Error;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::err_msg;

use crate::iter::HabitInstanceStoreIdIterator;


@@ 257,7 258,9 @@ impl HabitTemplate for Entry {
}

fn instance_id_for_name_and_datestr(habit_name: &String, habit_date: &String) -> Result<StoreId> {
    crate::module_path::new_id(format!("instance/{}-{}", habit_name, habit_date)).map_err(Error::from)
    crate::module_path::new_id(format!("instance/{}-{}", habit_name, habit_date))
        .context(format_err!("Failed building ID for instance: habit name = {}, habit date = {}", habit_name, habit_date))
        .map_err(Error::from)
}

pub mod builder {

M lib/domain/libimaghabit/src/iter.rs => lib/domain/libimaghabit/src/iter.rs +3 -2
@@ 19,6 19,7 @@

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

use libimagstore::storeid::StoreIdIterator;
use libimagstore::storeid::StoreIdIteratorWithStore;


@@ 33,11 34,11 @@ impl Iterator for HabitTemplateStoreIdIterator {

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(n) = self.0.next() {
            match n {
            match n.context("Error while iterating").map_err(Error::from) {
                Ok(n) => if n.is_habit_template() {
                    return Some(Ok(n))
                },
                Err(e) => return Some(Err(e).map_err(Error::from)),
                Err(e) => return Some(Err(e)),
            }
        }
        None

M lib/domain/libimaghabit/src/util.rs => lib/domain/libimaghabit/src/util.rs +2 -0
@@ 21,6 21,7 @@ use std::ops::BitXor;

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

use crate::habit::HabitTemplate;
use crate::instance::HabitInstance;


@@ 88,6 89,7 @@ pub fn get_string_header_from_entry(e: &Entry, path: &'static str) -> Result<Str
    e.get_header()
        .read_string(path)?
        .ok_or_else(|| EM::EntryHeaderFieldMissing(path))
        .context(format_err!("Error while reading header '{}' from '{}'", path, e.get_location()))
        .map_err(Error::from)
}


M lib/domain/libimaglog/src/log.rs => lib/domain/libimaglog/src/log.rs +3 -1
@@ 24,6 24,7 @@ use libimagentryutil::isa::IsKindHeaderPathProvider;

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


use toml::Value;


@@ 38,12 39,13 @@ provide_kindflag_path!(pub IsLog, "log.is_log");

impl Log for Entry {
    fn is_log(&self) -> Result<bool> {
        self.is::<IsLog>().map_err(From::from)
        self.is::<IsLog>().context("Cannot check whether Entry is a Log").map_err(From::from)
    }

    fn make_log_entry(&mut self) -> Result<()> {
        self.get_header_mut()
            .insert("log.is_log", Value::Boolean(true))
            .context("Cannot insert 'log.is_log' into header of entry")
            .map_err(Error::from)
            .map(|_| ())
    }

M lib/domain/libimagmail/src/mail.rs => lib/domain/libimagmail/src/mail.rs +22 -12
@@ 57,12 57,17 @@ impl Mail for Entry {
            .context(format_err!("Cannot parse Email {}", mail_file_location.display()))?
            .headers
            .into_iter()
            .filter_map(|hdr| match hdr.get_key() {
                Err(e) => Some(Err(e).map_err(Error::from)),
                Ok(k) => if k == field {
                    Some(Ok(hdr))
                } else {
                    None
            .filter_map(|hdr| {
                match hdr.get_key()
                    .context(format_err!("Cannot fetch key '{}' from Email {}", field, mail_file_location.display()))
                    .map_err(Error::from)
                {
                    Ok(k) => if k == field {
                        Some(Ok(hdr))
                    } else {
                        None
                    },
                    Err(e) => Some(Err(e)),
                }
            })
            .next()


@@ 135,12 140,17 @@ impl<'a> MailHeader<'a> {
    pub fn get_field(&self, field: &str) -> Result<Option<String>> {
        match self.0
            .iter()
            .filter_map(|hdr| match hdr.get_key() {
                Err(e) => Some(Err(e).map_err(Error::from)),
                Ok(key) => if key == field {
                    Some(Ok(hdr))
                } else {
                    None
            .filter_map(|hdr| {
                match hdr.get_key()
                    .context(format_err!("Cannot get field {}", field))
                    .map_err(Error::from)
                {
                    Ok(key) => if key == field {
                        Some(Ok(hdr))
                    } else {
                        None
                    },
                    Err(e) => Some(Err(e))
                }
            })
            .next()

M lib/domain/libimagmail/src/util.rs => lib/domain/libimagmail/src/util.rs +5 -4
@@ 39,8 39,7 @@ pub(crate) fn get_message_header_at_key<P: AsRef<Path>, K: AsRef<str>>(p: P, k: 
        .context(format_err!("Cannot parse Email {}", p.as_ref().display()))?
        .headers
        .into_iter()
        .filter_map(|hdr| match hdr.get_key() {
            Err(e) => Some(Err(e).map_err(Error::from)),
        .filter_map(|hdr| match hdr.get_key().context("Cannot get key from mail header").map_err(Error::from) {
            Ok(key) => {
                let lower_key = key.to_lowercase();
                trace!("Test: {} == {}", lower_key, k.as_ref());


@@ 49,11 48,12 @@ pub(crate) fn get_message_header_at_key<P: AsRef<Path>, K: AsRef<str>>(p: P, k: 
                } else {
                    None
                }
            }
            },
            Err(e) => Some(Err(e))
        })
        .next()
        .ok_or_else(|| format_err!("'{}' not found in {}", k.as_ref(), p.as_ref().display()))?
        .and_then(|hdr| hdr.get_value().map_err(Error::from))
        .and_then(|hdr| hdr.get_value().context("Cannot get value from mail header").map_err(Error::from))
}

pub(crate) fn get_message_id_for_mailfile<P: AsRef<Path>>(p: P) -> Result<String> {


@@ 77,6 77,7 @@ pub fn get_mail_text_content<P: AsRef<Path>>(p: P) -> Result<String> {
    ::mailparse::parse_mail(::std::fs::read_to_string(p.as_ref())?.as_bytes())
        .context(format_err!("Cannot parse Email {}", p.as_ref().display()))?
        .get_body()
        .context("Cannot get body of mail")
        .map_err(Error::from)
}


M lib/domain/libimagnotes/src/iter.rs => lib/domain/libimagnotes/src/iter.rs +3 -2
@@ 23,6 23,7 @@ use libimagstore::storeid::StoreIdIterator;
use crate::notestoreid::*;
use failure::Fallible as Result;
use failure::Error;
use failure::ResultExt;

#[derive(Debug)]
pub struct NoteIterator(StoreIdIterator);


@@ 40,11 41,11 @@ impl Iterator for NoteIterator {

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(n) = self.0.next() {
            match n {
            match n.context("Error while iterating").map_err(Error::from) {
                Ok(n) => if n.is_note_id() {
                    return Some(Ok(n));
                },
                Err(e) => return Some(Err(e).map_err(Error::from)),
                Err(e) => return Some(Err(e)),
            }
        }


M lib/domain/libimagnotes/src/note.rs => lib/domain/libimagnotes/src/note.rs +2 -0
@@ 27,6 27,7 @@ use toml_query::set::TomlValueSetExt;

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

pub trait Note {
    fn set_name(&mut self, n: String) -> Result<()>;


@@ 40,6 41,7 @@ impl Note for Entry {
    fn set_name(&mut self, n: String) -> Result<()> {
        self.get_header_mut()
            .set("note.name", Value::String(n))
            .context(format_err!("Cannot set 'note.name' in header of {}", self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }

M lib/domain/libimagtimetrack/src/iter/create.rs => lib/domain/libimagtimetrack/src/iter/create.rs +2 -0
@@ 21,6 21,7 @@ use toml::Value;
use toml_query::insert::TomlValueInsertExt;
use chrono::naive::NaiveDateTime as NDT;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;

use crate::constants::*;


@@ 60,6 61,7 @@ impl<'a> Iterator for CreateTimeTrackIter<'a>
                res.and_then(|(id, starttime)| {
                    self.store
                        .create(id)
                        .context("Failed to create entry")
                        .map_err(Error::from)
                        .and_then(|mut entry| {
                            let v = Value::String(starttime.format(DATE_TIME_FORMAT).to_string());

M lib/domain/libimagtimetrack/src/timetracking.rs => lib/domain/libimagtimetrack/src/timetracking.rs +19 -1
@@ 37,6 37,7 @@ use toml_query::delete::TomlValueDeleteExt;
use toml_query::insert::TomlValueInsertExt;
use toml_query::read::TomlValueReadTypeExt;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;

pub trait TimeTracking {


@@ 64,6 65,8 @@ impl TimeTracking for Entry {
    fn get_timetrack_tag(&self) -> Result<TTT> {
        self.get_header()
            .read_string(DATE_TIME_TAG_HEADER_PATH)
            .context(format_err!("Failed to read header '{}' of {}", DATE_TIME_TAG_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)?
            .ok_or_else(|| Error::from(EM::EntryHeaderReadError))
            .map(Into::into)


@@ 74,6 77,8 @@ impl TimeTracking for Entry {

        self.get_header_mut()
            .insert(DATE_TIME_START_HEADER_PATH, Value::String(s))
            .context(format_err!("Failed get header '{}' of {}", DATE_TIME_START_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }


@@ 81,6 86,8 @@ impl TimeTracking for Entry {
    fn get_start_datetime(&self) -> Result<Option<NaiveDateTime>> {
        self.get_header()
            .read_string(DATE_TIME_START_HEADER_PATH)
            .context(format_err!("Failed read header '{}' of {}", DATE_TIME_START_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)
            .and_then(header_value_to_dt)
    }


@@ 88,6 95,8 @@ impl TimeTracking for Entry {
    fn delete_start_datetime(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(DATE_TIME_START_HEADER_PATH)
            .context(format_err!("Failed delete header '{}' of {}", DATE_TIME_START_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }


@@ 97,6 106,8 @@ impl TimeTracking for Entry {

        self.get_header_mut()
            .insert(DATE_TIME_END_HEADER_PATH, Value::String(s))
            .context(format_err!("Failed insert header '{}' in {}", DATE_TIME_END_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }


@@ 104,6 115,8 @@ impl TimeTracking for Entry {
    fn get_end_datetime(&self) -> Result<Option<NaiveDateTime>> {
        self.get_header()
            .read_string(DATE_TIME_END_HEADER_PATH)
            .context(format_err!("Failed read header '{}' of {}", DATE_TIME_END_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)
            .and_then(header_value_to_dt)
    }


@@ 111,6 124,8 @@ impl TimeTracking for Entry {
    fn delete_end_datetime(&mut self) -> Result<()> {
        self.get_header_mut()
            .delete(DATE_TIME_END_HEADER_PATH)
            .context(format_err!("Failed delete header '{}' of {}", DATE_TIME_END_HEADER_PATH,
                                 self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }


@@ 135,7 150,10 @@ impl TimeTracking for Entry {

fn header_value_to_dt(val: Option<String>) -> Result<Option<NaiveDateTime>> {
    match val {
        Some(ref s) => NaiveDateTime::parse_from_str(s, DATE_TIME_FORMAT).map_err(Error::from).map(Some),
        Some(ref s) => NaiveDateTime::parse_from_str(s, DATE_TIME_FORMAT)
            .context(format_err!("Failed to parse '{}' datetime with format '{}'",
                                 s, DATE_TIME_FORMAT))
            .map_err(Error::from).map(Some),
        None => Ok(None),
    }
}

M lib/domain/libimagtimetrack/src/timetrackingstore.rs => lib/domain/libimagtimetrack/src/timetrackingstore.rs +3 -0
@@ 26,6 26,7 @@ use chrono::NaiveDateTime as NDT;
use toml::Value;
use toml_query::insert::TomlValueInsertExt;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;

use libimagstore::store::Store;


@@ 70,6 71,8 @@ impl<'a> TimeTrackStore<'a> for Store {
        use std::path::PathBuf;

        COMPILER.compile(CRATE_NAME, start)
            .context(format_err!("Failed to compile DatePath for crate '{}' with start = '{}'",
                                 CRATE_NAME, start))
            .map_err(Error::from)
            .map(|mut id| {
                id.local_push(PathBuf::from(ts.as_str()));

M lib/domain/libimagwiki/src/wiki.rs => lib/domain/libimagwiki/src/wiki.rs +2 -0
@@ 27,6 27,7 @@ use libimagentrylink::internal::InternalLinker;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use failure::ResultExt;

pub struct Wiki<'a, 'b>(&'a Store, &'b str);



@@ 58,6 59,7 @@ impl<'a, 'b> Wiki<'a, 'b> {

        self.0
            .get(sid)
            .context("Cannot get ID from store")
            .map_err(Error::from)?
            .ok_or_else(|| Error::from(err_msg("Missing index")))
    }

M lib/entry/libimagentrycategory/src/category.rs => lib/entry/libimagentrycategory/src/category.rs +2 -0
@@ 27,6 27,7 @@ use libimagentrylink::internal::InternalLinker;
use toml_query::read::TomlValueReadTypeExt;

use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use failure::err_msg;
use crate::store::CATEGORY_REGISTER_NAME_FIELD_PATH;


@@ 49,6 50,7 @@ impl Category for Entry {
        trace!("Getting category name of '{:?}'", self.get_location());
        self.get_header()
            .read_string(CATEGORY_REGISTER_NAME_FIELD_PATH)
            .context(format_err!("Failed to read header at '{}'", CATEGORY_REGISTER_NAME_FIELD_PATH))
            .map_err(Error::from)?
            .ok_or_else(|| Error::from(err_msg("Category name missing")))
    }

M lib/entry/libimagentrycategory/src/entry.rs => lib/entry/libimagentrycategory/src/entry.rs +3 -3
@@ 52,7 52,7 @@ impl EntryCategory for Entry {
        trace!("Setting category '{}' UNCHECKED", s);
        self.get_header_mut()
            .insert(&String::from("category.value"), Value::String(s.to_string()))
            .map_err(Error::from)
            .context(format_err!("Failed to insert header at 'category.value' of '{}'", self.get_location()))
            .context(EM::EntryHeaderWriteError)
            .map_err(Error::from)
            .map(|_| ())


@@ 84,7 84,7 @@ impl EntryCategory for Entry {
        trace!("Has category? '{}'", self.get_location());
        self.get_header()
            .read("category.value")
            .map_err(Error::from)
            .context(format_err!("Failed to read header at 'category.value' of '{}'", self.get_location()))
            .context(EM::EntryHeaderReadError)
            .map_err(Error::from)
            .map(|x| x.is_some())


@@ 101,7 101,7 @@ impl EntryCategory for Entry {

        self.get_header_mut()
            .delete("category.value")
            .map_err(Error::from)
            .context(format_err!("Failed to delete header at 'category.value' of '{}'", self.get_location()))
            .context(EM::EntryHeaderWriteError)
            .map_err(Error::from)
            .map(|_| ())

M lib/entry/libimagentrycategory/src/iter.rs => lib/entry/libimagentrycategory/src/iter.rs +5 -7
@@ 20,7 20,6 @@
use libimagstore::storeid::StoreIdIterator;
use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagerror::errors::ErrorMsg as EM;

use toml_query::read::TomlValueReadTypeExt;



@@ 60,8 59,8 @@ impl<'a> Iterator for CategoryNameIter<'a> {
        let query = CATEGORY_REGISTER_NAME_FIELD_PATH;

        while let Some(sid) = self.1.next() {
            match sid {
                Err(e) => return Some(Err(e).map_err(Error::from)),
            match sid.context("Error while iterating over category names").map_err(Error::from) {
                Err(e) => return Some(Err(e)),
                Ok(sid) => {
                    if sid.is_in_collection(&["category"]) {
                        let func = |store: &Store| { // hack for returning Some(Result<_, _>)


@@ 70,8 69,7 @@ impl<'a> Iterator for CategoryNameIter<'a> {
                                .ok_or_else(|| err_msg("Store read error"))?
                                .get_header()
                                .read_string(query)
                                .map_err(Error::from)
                                .context(EM::EntryHeaderReadError)?
                                .context(format_err!("Failed to read header at '{}'", query))?
                                .ok_or_else(|| err_msg("Store read error"))
                                .map_err(Error::from)
                        };


@@ 99,8 97,8 @@ impl<'a> Iterator for CategoryEntryIterator<'a> {

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(next) = self.1.next() {
            match next {
                Err(e) => return Some(Err(e).map_err(Error::from)),
            match next.context("Error while iterating over category entries").map_err(Error::from) {
                Err(e) => return Some(Err(e)),
                Ok(next) => {
                    let getter = |next| -> Result<(String, FileLockEntry<'a>)> {
                        let entry = self.0

M lib/entry/libimagentrydatetime/src/datetime.rs => lib/entry/libimagentrydatetime/src/datetime.rs +3 -0
@@ 92,6 92,9 @@ impl EntryDate for Entry {

        self.get_header_mut()
            .insert(&DATE_HEADER_LOCATION, Value::String(date))
            .context(format_err!("Failed to insert header '{}' in '{}'",
                                 *DATE_HEADER_LOCATION,
                                 self.get_location()))
            .map_err(Error::from)
            .map(|opt| opt.map(|stri| {
                stri.as_str()

M lib/entry/libimagentrydatetime/src/lib.rs => lib/entry/libimagentrydatetime/src/lib.rs +1 -1
@@ 38,10 38,10 @@
)]

#[macro_use] extern crate lazy_static;
#[macro_use] extern crate failure;
extern crate chrono;
extern crate toml_query;
extern crate toml;
extern crate failure;

extern crate libimagerror;
extern crate libimagstore;

M lib/entry/libimagentryedit/src/edit.rs => lib/entry/libimagentryedit/src/edit.rs +3 -1
@@ 66,7 66,9 @@ impl EditHeader for Entry {
    fn edit_header_and_content(&mut self, rt: &Runtime) -> Result<()> {
        let mut header_and_content = self.to_str()?;
        let _                      = edit_in_tmpfile(rt, &mut header_and_content)?;
        self.replace_from_buffer(&header_and_content).map_err(Error::from)
        self.replace_from_buffer(&header_and_content)
            .context("Failed to replace header and content from buffer")
            .map_err(Error::from)
    }

}

M lib/entry/libimagentryfilter/src/builtin/header/field_exists.rs => lib/entry/libimagentryfilter/src/builtin/header/field_exists.rs +4 -0
@@ 23,6 23,7 @@ use toml_query::read::TomlValueReadExt;
use filters::failable::filter::FailableFilter;

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

use crate::builtin::header::field_path::FieldPath;


@@ 47,6 48,9 @@ impl FailableFilter<Entry> for FieldExists {
    fn filter(&self, e: &Entry) -> Result<bool> {
        e.get_header()
            .read(&self.header_field_path[..])
            .context(format_err!("Failed reading header '{}' in '{}'",
                                 self.header_field_path,
                                 e.get_location()))
            .map_err(Error::from)
            .map(|o| o.is_some())
    }

M lib/entry/libimagentryfilter/src/lib.rs => lib/entry/libimagentryfilter/src/lib.rs +1 -1
@@ 40,7 40,7 @@ extern crate regex;
extern crate semver;
extern crate toml;
extern crate toml_query;
extern crate failure;
#[macro_use] extern crate failure;

extern crate libimagstore;
extern crate libimagentrytag;

M lib/entry/libimagentrygps/src/entry.rs => lib/entry/libimagentrygps/src/entry.rs +7 -2
@@ 60,11 60,16 @@ impl GPSEntry for Entry {
        self.get_header_mut()
            .insert("gps.coordinates", c.into())
            .map(|_| ())
            .context(format_err!("Error while inserting header 'gps.coordinates' in '{}'", self.get_location()))
            .map_err(Error::from)
    }

    fn get_coordinates(&self) -> Result<Option<Coordinates>> {
        match self.get_header().read("gps.coordinates").map_err(Error::from)?  {
        match self
            .get_header()
            .read("gps.coordinates")
            .context(format_err!("Error while reading header 'gps.coordinates' in '{}'", self.get_location()))?
        {
            Some(hdr) => Coordinates::from_value(hdr).map(Some),
            None      => Ok(None),
        }


@@ 89,7 94,7 @@ impl GPSEntry for Entry {
        let hdr = self.get_header_mut();
        for pattern in patterns.iter() {
            let _ = hdr.delete(pattern)
                .map_err(Error::from)
                .context(format_err!("Error while deleting header '{}'", pattern))
                .context("Error writing header")?;
        }


M lib/entry/libimagentrygps/src/lib.rs => lib/entry/libimagentrygps/src/lib.rs +1 -1
@@ 38,7 38,7 @@
extern crate toml;
extern crate toml_query;
#[macro_use] extern crate serde_derive;
extern crate failure;
#[macro_use] extern crate failure;

extern crate libimagstore;
extern crate libimagerror;

M lib/entry/libimagentrylink/src/external.rs => lib/entry/libimagentrylink/src/external.rs +4 -1
@@ 70,7 70,7 @@ impl Link for Entry {
    fn get_link_uri_from_filelockentry(&self) -> Result<Option<Url>> {
        self.get_header()
            .read_string("links.external.content.url")
            .map_err(Error::from)
            .context(format_err!("Error reading header 'links.external.content.url' from '{}'", self.get_location()))
            .context(EM::EntryHeaderReadError)
            .map_err(Error::from)
            .and_then(|opt| match opt {


@@ 79,11 79,13 @@ impl Link for Entry {
                    debug!("Found url, parsing: {:?}", s);
                    Url::parse(&s[..])
                        .map_err(Error::from)
                        .context(format_err!("Failed to parse URL: '{}'", s))
                        .context(err_msg("Invalid URI"))
                        .map_err(Error::from)
                        .map(Some)
                },
            })
            .context("Failed to get link URI from entry")
            .map_err(Error::from)
    }



@@ 91,6 93,7 @@ impl Link for Entry {
        match self.get_header().read_string("links.external.url")? {
            None        => Ok(None),
            Some(ref s) => Url::parse(&s[..])
                .context(format_err!("Failed to parse URL: '{}'", s))
                .map(Some)
                .map_err(Error::from)
                .context(EM::EntryHeaderReadError)

M lib/entry/libimagentrylink/src/internal.rs => lib/entry/libimagentrylink/src/internal.rs +5 -5
@@ 265,7 265,7 @@ impl InternalLinker for Entry {
        let res = self
            .get_header()
            .read("links.internal")
            .map_err(Error::from)
            .context(format_err!("Failed to read header 'links.internal' of '{}'", self.get_location()))
            .context(EM::EntryHeaderReadError)
            .context(EM::EntryHeaderError)
            .map_err(Error::from)


@@ 299,7 299,7 @@ impl InternalLinker for Entry {
        let res = self
            .get_header_mut()
            .insert("links.internal", Value::Array(new_links))
            .map_err(Error::from)
            .context(format_err!("Failed to insert header 'links.internal' of '{}'", self.get_location()))
            .context(EM::EntryHeaderReadError)
            .map_err(Error::from);
        process_rw_result(res)


@@ 336,7 336,7 @@ impl InternalLinker for Entry {

    fn unlink(&mut self, store: &Store) -> Result<()> {
        for id in self.get_internal_links()?.map(|l| l.get_store_id().clone()) {
            match store.get(id).map_err(Error::from)? {
            match store.get(id).context("Failed to get entry")? {
                Some(mut entry) => self.remove_internal_link(&mut entry)?,
                None            => return Err(err_msg("Link target does not exist")),
            }


@@ 382,7 382,7 @@ fn rewrite_links<I: Iterator<Item = Link>>(header: &mut Value, links: I) -> Resu
    debug!("Setting new link array: {:?}", links);
    let process = header
        .insert("links.internal", Value::Array(links))
        .map_err(Error::from)
        .context(format_err!("Failed to insert header 'links.internal'"))
        .context(EM::EntryHeaderReadError)
        .map_err(Error::from);
    process_rw_result(process).map(|_| ())


@@ 409,7 409,7 @@ fn add_foreign_link(target: &mut Entry, from: StoreId) -> Result<()> {
            let res = target
                .get_header_mut()
                .insert("links.internal", Value::Array(links))
                .map_err(Error::from)
                .context(format_err!("Failed to insert header 'links.internal'"))
                .context(EM::EntryHeaderReadError)
                .map_err(Error::from);


M lib/entry/libimagentryref/src/reference.rs => lib/entry/libimagentryref/src/reference.rs +13 -8
@@ 89,6 89,7 @@ pub mod fassade {
    use libimagentryutil::isa::Is;

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

    use crate::hasher::sha1::Sha1Hasher;


@@ 104,7 105,7 @@ pub mod fassade {
    impl RefFassade for Entry {
        /// Check whether the underlying object is actually a ref
        fn is_ref(&self) -> Result<bool> {
            self.is::<IsRef>().map_err(Error::from)
            self.is::<IsRef>().context("Failed to check is-ref flag").map_err(Error::from)
        }

        fn as_ref_with_hasher<H: Hasher>(&self)     -> RefWithHasher<H> {


@@ 163,7 164,7 @@ impl<'a, H: Hasher> Ref for RefWithHasher<'a, H> {

    /// Check whether the underlying object is actually a ref
    fn is_ref(&self) -> Result<bool> {
        self.0.is::<IsRef>().map_err(Error::from)
        self.0.is::<IsRef>().context("Failed to check is-ref flag").map_err(Error::from)
    }

    fn get_hash(&self) -> Result<&str> {


@@ 171,6 172,7 @@ impl<'a, H: Hasher> Ref for RefWithHasher<'a, H> {
        self.0
            .get_header()
            .read(&header_path)
            .context(format_err!("Failed to read header at '{}'", header_path))
            .map_err(Error::from)?
            .ok_or_else(|| {
                Error::from(EM::EntryHeaderFieldMissing("ref.hash.<hash>"))


@@ 210,7 212,7 @@ impl<'a, H: Hasher> Ref for RefWithHasher<'a, H> {
        self.0
            .get_header()
            .read("ref.relpath")
            .map_err(Error::from)?
            .context("Failed to read header at 'ref.relpath'")?
            .ok_or_else(|| Error::from(EM::EntryHeaderFieldMissing("ref.relpath")))
            .and_then(|v| {
                v.as_str()


@@ 223,19 225,20 @@ impl<'a, H: Hasher> Ref for RefWithHasher<'a, H> {
    fn hash_valid(&self, config: &Config) -> Result<bool> {
        let ref_header = self.0
            .get_header()
            .read("ref")?
            .read("ref")
            .context("Failed to read header at 'ref'")?
            .ok_or_else(|| err_msg("Header missing at 'ref'"))?;

        let basepath_name = ref_header
            .read("basepath")
            .map_err(Error::from)?
            .context("Failed to read header at 'ref.basepath'")?
            .ok_or_else(|| err_msg("Header missing at 'ref.basepath'"))?
            .as_str()
            .ok_or_else(|| Error::from(EM::EntryHeaderTypeError2("ref.hash.<hash>", "string")))?;

        let path = ref_header
            .read("relpath")
            .map_err(Error::from)?
            .context("Failed to read header at 'ref.relpath'")?
            .ok_or_else(|| err_msg("Header missing at 'ref.relpath'"))?
            .as_str()
            .map(PathBuf::from)


@@ 246,7 249,7 @@ impl<'a, H: Hasher> Ref for RefWithHasher<'a, H> {

        ref_header
            .read(H::NAME)
            .map_err(Error::from)?
            .context(format_err!("Failed to read header at 'ref.{}'", H::NAME))?
            .ok_or_else(|| format_err!("Header missing at 'ref.{}'", H::NAME))
            .and_then(|v| {
                v.as_str().ok_or_else(|| {


@@ 352,7 355,9 @@ impl<'a, H> MutRef for MutRefWithHasher<'a, H>
                trace!("Using relpath = {} to make header section", relpath.display());
                make_header_section(hash, H::NAME, relpath, basepath_name)
            })
            .and_then(|h| self.0.get_header_mut().insert("ref", h).map_err(Error::from))
            .and_then(|h| self.0.get_header_mut().insert("ref", h)
                      .context("Failed to insert 'ref' in header")
                      .map_err(Error::from))
            .and_then(|_| self.0.set_isflag::<IsRef>())
            .context("Making ref out of entry")?;


M lib/entry/libimagentrytag/src/tagable.rs => lib/entry/libimagentrytag/src/tagable.rs +1 -0
@@ 51,6 51,7 @@ impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.read("tag.values")
            .context(format_err!("Failed to read header at 'tag.values'"))
            .map_err(Error::from)
            .context(EM::EntryHeaderReadError)?
            .map(|val| {

M lib/entry/libimagentryutil/src/isa.rs => lib/entry/libimagentryutil/src/isa.rs +9 -1
@@ 18,6 18,7 @@
//

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

use toml::Value;


@@ 79,7 80,12 @@ impl Is for ::libimagstore::store::Entry {
    fn is<T: IsKindHeaderPathProvider>(&self) -> Result<bool> {
        let field = T::kindflag_header_location();

        match self.get_header().read_bool(field).map_err(Error::from)? {
        match self
            .get_header()
            .read_bool(field)
            .context(format_err!("Failed reading header '{}' in '{}'", field, self.get_location()))
            .map_err(Error::from)?
        {
            Some(b) => Ok(b),
            None    => Ok(false),
        }


@@ 88,6 94,7 @@ impl Is for ::libimagstore::store::Entry {
    fn set_isflag<T: IsKindHeaderPathProvider>(&mut self) -> Result<()> {
        self.get_header_mut()
            .insert(T::kindflag_header_location(), Value::Boolean(true))
            .context(format_err!("Failed inserting header '{}' in '{}'", T::kindflag_header_location(), self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }


@@ 96,6 103,7 @@ impl Is for ::libimagstore::store::Entry {
        trace!("Trying to remove: {}", T::kindflag_header_location());
        self.get_header_mut()
            .delete(T::kindflag_header_location())
            .context(format_err!("Failed deleting header '{}' in '{}'", T::kindflag_header_location(), self.get_location()))
            .map_err(Error::from)
            .map(|_| ())
    }

M lib/entry/libimagentryutil/src/lib.rs => lib/entry/libimagentryutil/src/lib.rs +1 -1
@@ 40,7 40,7 @@
extern crate filters;
extern crate toml;
extern crate toml_query;
extern crate failure;
#[macro_use] extern crate failure;
#[macro_use] extern crate log;

extern crate libimagstore;

M lib/entry/libimagentryview/src/builtin/editor.rs => lib/entry/libimagentryview/src/builtin/editor.rs +1 -2
@@ 27,7 27,6 @@ use crate::viewer::Viewer;
use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use failure::err_msg;

pub struct EditorView<'a>(&'a Runtime<'a>);



@@ 43,7 42,7 @@ impl<'a> Viewer for EditorView<'a> {
    {
        let mut entry = e.to_str()?.clone().to_string();
        edit_in_tmpfile(self.0, &mut entry)
            .context(err_msg("Error while viewing"))
            .context("Error while viewing")
            .map_err(Error::from)
    }
}

M lib/etc/libimaginteraction/src/ask.rs => lib/etc/libimaginteraction/src/ask.rs +4 -2
@@ 178,10 178,12 @@ pub fn ask_select_from_list(list: &[&str]) -> Result<String> {
/// The `nl` parameter can be used to configure whether a newline character should be printed
pub fn ask_question(question: &str, nl: bool, output: &mut Write) -> Result<()> {
    if nl {
        writeln!(output, "[imag]: {}?", Yellow.paint(question)).map_err(Error::from)
        writeln!(output, "[imag]: {}?", Yellow.paint(question))
    } else {
        write!(output, "[imag]: {}?", Yellow.paint(question)).map_err(Error::from)
        write!(output, "[imag]: {}?", Yellow.paint(question))
    }
    .context("Failed to write question to output")
    .map_err(Error::from)
}

#[cfg(test)]