~matthiasbeyer/imag

03e0864268a843309ad7afb6c0c0681e7308d045 — Matthias Beyer 2 years ago d936b61 + 5302244
Merge branch 'unnamed-annotations' into master-ff

This merge adds several patches to improve libimagannotation and
imag-annotate.
M bin/core/imag-annotate/Cargo.toml => bin/core/imag-annotate/Cargo.toml +1 -0
@@ 32,6 32,7 @@ libimagstore           = { version = "0.10.0", path = "../../../lib/core/libimag
libimagrt              = { version = "0.10.0", path = "../../../lib/core/libimagrt" }
libimagerror           = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
libimagentryannotation = { version = "0.10.0", path = "../../../lib/entry/libimagentryannotation" }
libimagentrylink       = { version = "0.10.0", path = "../../../lib/entry/libimagentrylink" }
libimagentryedit       = { version = "0.10.0", path = "../../../lib/entry/libimagentryedit" }
libimagutil            = { version = "0.10.0", path = "../../../lib/etc/libimagutil" }


M bin/core/imag-annotate/src/main.rs => bin/core/imag-annotate/src/main.rs +55 -22
@@ 37,7 37,9 @@
extern crate clap;
#[macro_use]
extern crate log;
#[macro_use]
extern crate failure;
extern crate toml_query;

extern crate libimagentryannotation;
extern crate libimagentryedit;


@@ 45,10 47,12 @@ extern crate libimagerror;
#[macro_use] extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;
extern crate libimagentrylink;

use std::io::Write;

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

use libimagentryannotation::annotateable::*;
use libimagentryannotation::annotation_fetcher::*;


@@ 57,9 61,12 @@ use libimagerror::trace::MapErrTrace;
use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode;
use libimagerror::errors::ErrorMsg as EM;
use libimagerror::iter::TraceIterator;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::store::FileLockEntry;
use libimagstore::iter::get::StoreIdGetIteratorExtension;
use libimagentrylink::internal::InternalLinker;

mod ui;



@@ 89,23 96,48 @@ fn main() {
}

fn add(rt: &Runtime) {
    let scmd            = rt.cli().subcommand_matches("add").unwrap(); // safed by main()
    let annotation_name = scmd.value_of("annotation_name").unwrap(); // safed by clap
    let ids             = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1);
    let scmd    = rt.cli().subcommand_matches("add").unwrap(); // safed by main()
    let mut ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1).into_iter();

    ids.into_iter().for_each(|id| {
        let _ = rt.store()
            .get(id.clone())
    if let Some(first) = ids.next() {
        let mut annotation = rt.store()
            .get(first.clone())
            .map_err_trace_exit_unwrap(1)
            .ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
            .ok_or_else(|| EM::EntryNotFound(first.local_display_string()))
            .map_err(Error::from)
            .map_err_trace_exit_unwrap(1)
            .annotate(rt.store(), annotation_name)
            .map_err_trace_exit_unwrap(1)
            .edit_content(&rt)
            .annotate(rt.store())
            .map_err_trace_exit_unwrap(1);
    })

        let _ = annotation.edit_content(&rt).map_err_trace_exit_unwrap(1);

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

            let _ = entry.add_internal_link(&mut annotation).map_err_trace_exit_unwrap(1);
        }

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

fn remove(rt: &Runtime) {


@@ 162,25 194,26 @@ fn list(rt: &Runtime) {
                    .ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
                    .map_err(Error::from)
                    .map_err_trace_exit_unwrap(1)
                    .annotations(rt.store())
                    .annotations()
                    .map_err_trace_exit_unwrap(1)
                    .into_get_iter(rt.store())
                    .trace_unwrap_exit(1)
                    .map(|opt| opt.ok_or_else(|| format_err!("Cannot find entry")))
                    .trace_unwrap_exit(1)
                    .enumerate()
                    .map(|(i, a)| {
                        list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text)
                    })
                    .collect::<Vec<_>>();
                    .for_each(|(i, entry)| list_annotation(&rt, i, entry, with_text));
            });
    } else { // ids.len() == 0
        // show them all
        let _ = rt
            .store()
        rt.store()
            .all_annotations()
            .map_err_trace_exit_unwrap(1)
            .into_get_iter(rt.store())
            .trace_unwrap_exit(1)
            .map(|opt| opt.ok_or_else(|| format_err!("Cannot find entry")))
            .trace_unwrap_exit(1)
            .enumerate()
            .map(|(i, a)| {
                list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text)
            })
            .collect::<Vec<_>>();
            .for_each(|(i, entry)| list_annotation(&rt, i, entry, with_text));
    }
}


M bin/core/imag-annotate/src/ui.rs => bin/core/imag-annotate/src/ui.rs +9 -7
@@ 31,6 31,15 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
        .subcommand(SubCommand::with_name("add")
                    .about("Add annotation to an entry")
                    .version("0.1")

                    .arg(Arg::with_name("dont-print-name")
                         .short("N")
                         .long("no-name")
                         .takes_value(false)
                         .required(false)
                         .multiple(false)
                         .help("Do not print the name of the annotation after annotating.")
                         )
                    .arg(Arg::with_name("entry")
                         .index(1)
                         .takes_value(true)


@@ 38,13 47,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
                         .multiple(false)
                         .help("The entry to add the latitude/longitude to")
                         .value_name("ENTRY"))
                    .arg(Arg::with_name("annotation_name")
                         .index(2)
                         .takes_value(true)
                         .required(true)
                         .multiple(false)
                         .help("Name of the new annotation")
                         .value_name("NAME"))
                   )

        .subcommand(SubCommand::with_name("remove")

M lib/entry/libimagentryannotation/Cargo.toml => lib/entry/libimagentryannotation/Cargo.toml +3 -0
@@ 25,6 25,9 @@ toml = "0.4"
toml-query = "0.8"
failure        = "0.1"
failure_derive = "0.1"
uuid           = { version = "0.7", features = ["v4"] }
log            = "0.4.0"


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

M lib/entry/libimagentryannotation/src/annotateable.rs => lib/entry/libimagentryannotation/src/annotateable.rs +25 -25
@@ 18,6 18,7 @@
//

use toml::Value;
use uuid::Uuid;

use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;


@@ 28,7 29,6 @@ use libimagentrylink::internal::InternalLinker;
use libimagentryutil::isa::Is;
use libimagentryutil::isa::IsKindHeaderPathProvider;

use toml_query::read::TomlValueReadTypeExt;
use toml_query::insert::TomlValueInsertExt;

use failure::Fallible as Result;


@@ 36,12 36,12 @@ use failure::ResultExt;
use failure::Error;
use failure::err_msg;

use iter::*;
use module_path::ModuleEntryPath;

pub trait Annotateable {
    fn annotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<FileLockEntry<'a>>;
    fn annotate<'a>(&mut self, store: &'a Store) -> Result<FileLockEntry<'a>>;
    fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>>;
    fn annotations<'a>(&self, store: &'a Store) -> Result<AnnotationIter<'a>>;
    fn annotations(&self) -> Result<StoreIdIterator>;
    fn is_annotation(&self) -> Result<bool>;
}



@@ 50,9 50,11 @@ provide_kindflag_path!(IsAnnotation, "annotation.is_annotation");
impl Annotateable for Entry {

    /// Annotate an entry, returns the new entry which is used to annotate
    fn annotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<FileLockEntry<'a>> {
        use module_path::ModuleEntryPath;
        store.retrieve(ModuleEntryPath::new(ann_name).into_storeid()?)
    fn annotate<'a>(&mut self, store: &'a Store) -> Result<FileLockEntry<'a>> {
        let ann_name = Uuid::new_v4().to_hyphenated().to_string();
        debug!("Creating annotation with name = {}", ann_name);

        store.retrieve(ModuleEntryPath::new(ann_name.clone()).into_storeid()?)
            .and_then(|mut anno| {
                {
                    let _ = anno.set_isflag::<IsAnnotation>()?;


@@ 70,30 72,28 @@ impl Annotateable for Entry {
            })
    }

    /// Checks the current entry for all annotations and removes the one where the name is
    /// `ann_name`, which is then returned
    // Removes the annotation `ann_name` from the current entry.
    // Fails if there's no such annotation entry or if the link to that annotation entry does not
    // exist.
    fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>> {
        for annotation in self.annotations(store)? {
            let mut anno = annotation?;
            let name = match anno.get_header().read_string("annotation.name")? {
                Some(ref name) => name.clone(),
                None           => continue,
            };

            if name == ann_name {
                let _ = self.remove_internal_link(&mut anno)?;
                return Ok(Some(anno));
            }
        if let Some(mut annotation) = store.get(ModuleEntryPath::new(ann_name).into_storeid()?)? {
            let _ = self.remove_internal_link(&mut annotation)?;
            Ok(Some(annotation))
        } else {
            // error: annotation does not exist
            Err(format_err!("Annotation '{}' does not exist", ann_name)).map_err(Error::from)
        }

        Ok(None)
    }

    /// Get all annotations of an entry
    fn annotations<'a>(&self, store: &'a Store) -> Result<AnnotationIter<'a>> {
    fn annotations(&self) -> Result<StoreIdIterator> {
        self.get_internal_links()
            .map(|iter| StoreIdIterator::new(Box::new(iter.map(|e| e.get_store_id().clone()).map(Ok))))
            .map(|i| AnnotationIter::new(i, store))
            .map(|it| {
                it.filter(|link| link.get_store_id().is_in_collection(&["annotation"]))
                    .map(|link| Ok(link.get_store_id().clone()))
            })
            .map(Box::new)
            .map(|inner| StoreIdIterator::new(inner))
    }

    fn is_annotation(&self) -> Result<bool> {

M lib/entry/libimagentryannotation/src/annotation_fetcher.rs => lib/entry/libimagentryannotation/src/annotation_fetcher.rs +6 -10
@@ 18,21 18,17 @@
//

use libimagstore::store::Store;
use libimagstore::storeid::StoreIdIterator;

use failure::Fallible as Result;
use iter::*;

pub trait AnnotationFetcher<'a> {

    fn all_annotations(&'a self) -> Result<AnnotationIter<'a>>;

pub trait AnnotationFetcher {
    fn all_annotations(&self) -> Result<StoreIdIterator>;
}

impl<'a> AnnotationFetcher<'a> for Store {

    fn all_annotations(&'a self) -> Result<AnnotationIter<'a>> {
        Ok(AnnotationIter::new(self.entries()?.without_store(), self))
impl<'a> AnnotationFetcher for Store {
    fn all_annotations(&self) -> Result<StoreIdIterator> {
        self.entries().map(|iter| iter.in_collection("annotation").without_store())
    }

}


D lib/entry/libimagentryannotation/src/iter.rs => lib/entry/libimagentryannotation/src/iter.rs +0 -77
@@ 1,77 0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2019 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 toml_query::read::TomlValueReadTypeExt;

use libimagstore::store::Store;
use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreIdIterator;
use libimagerror::errors::ErrorMsg as EM;

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

#[derive(Debug)]
pub struct AnnotationIter<'a>(StoreIdIterator, &'a Store);

impl<'a> AnnotationIter<'a> {

    pub fn new(iter: StoreIdIterator, store: &'a Store) -> AnnotationIter<'a> {
        AnnotationIter(iter, store)
    }

}

impl<'a> Iterator for AnnotationIter<'a> {
    type Item = Result<FileLockEntry<'a>>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.0.next() {
                None         => return None, // iterator consumed
                Some(Err(e)) => return Some(Err(e).map_err(Error::from)),
                Some(Ok(id)) => match self.1.get(id) {
                    Err(e) => {
                        return Some(Err(e)
                                    .context(err_msg("Store read error"))
                                    .map_err(Error::from))
                    },
                    Ok(Some(entry)) => {
                        match entry
                            .get_header()
                            .read_bool("annotation.is_annotation")
                            .context(EM::EntryHeaderReadError)
                            .map_err(Error::from)
                        {
                            Ok(None)        => continue, // not an annotation
                            Ok(Some(false)) => continue,
                            Ok(Some(true))  => return Some(Ok(entry)),
                            Err(e)          => return Some(Err(e)),
                        }
                    },
                    Ok(None) => continue,
                }
            }
        }
    }

}


M lib/entry/libimagentryannotation/src/lib.rs => lib/entry/libimagentryannotation/src/lib.rs +3 -2
@@ 39,7 39,9 @@

extern crate toml;
extern crate toml_query;
extern crate failure;
#[macro_use] extern crate failure;
#[macro_use] extern crate log;
extern crate uuid;

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


@@ 50,5 52,4 @@ module_entry_path_mod!("annotations");

pub mod annotateable;
pub mod annotation_fetcher;
pub mod iter;