Skip to main content

libimagentryannotation/
annotateable.rs

1//
2// imag - the personal information management suite for the commandline
3// Copyright (C) 2015-2020 Matthias Beyer <mail@beyermatthias.de> and contributors
4//
5// This library is free software; you can redistribute it and/or
6// modify it under the terms of the GNU Lesser General Public
7// License as published by the Free Software Foundation; version
8// 2.1 of the License.
9//
10// This library is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13// Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public
16// License along with this library; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18//
19
20use toml::Value;
21use uuid::Uuid;
22
23use libimagstore::store::Entry;
24use libimagstore::store::FileLockEntry;
25use libimagstore::store::Store;
26use libimagstore::storeid::StoreIdIterator;
27use libimagentrylink::linkable::Linkable;
28use libimagentryutil::isa::Is;
29use libimagentryutil::isa::IsKindHeaderPathProvider;
30
31use toml_query::insert::TomlValueInsertExt;
32
33use failure::Fallible as Result;
34use failure::ResultExt;
35use failure::Error;
36use failure::err_msg;
37
38pub trait Annotateable {
39    fn annotate<'a>(&mut self, store: &'a Store) -> Result<FileLockEntry<'a>>;
40    fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>>;
41    fn annotations(&self) -> Result<StoreIdIterator>;
42    fn is_annotation(&self) -> Result<bool>;
43}
44
45provide_kindflag_path!(IsAnnotation, "annotation.is_annotation");
46
47impl Annotateable for Entry {
48
49    /// Annotate an entry, returns the new entry which is used to annotate
50    fn annotate<'a>(&mut self, store: &'a Store) -> Result<FileLockEntry<'a>> {
51        let ann_name = Uuid::new_v4().to_hyphenated().to_string();
52        debug!("Creating annotation with name = {}", ann_name);
53
54        store.retrieve(crate::module_path::new_id(ann_name.clone())?)
55            .and_then(|mut anno| {
56                {
57                    anno.set_isflag::<IsAnnotation>()?;
58                    let _ = anno
59                        .get_header_mut()
60                        .insert("annotation.name", Value::String(ann_name))?;
61                }
62                Ok(anno)
63            })
64            .and_then(|mut anno| {
65                anno.add_link(self)
66                    .context(err_msg("Linking error"))
67                    .map_err(Error::from)
68                    .map(|_| anno)
69            })
70    }
71
72    // Removes the annotation `ann_name` from the current entry.
73    // Fails if there's no such annotation entry or if the link to that annotation entry does not
74    // exist.
75    fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>> {
76        if let Some(mut annotation) = store.get(crate::module_path::new_id(ann_name)?)? {
77            self.remove_link(&mut annotation)?;
78            Ok(Some(annotation))
79        } else {
80            // error: annotation does not exist
81            Err(format_err!("Annotation '{}' does not exist", ann_name)).map_err(Error::from)
82        }
83    }
84
85    /// Get all annotations of an entry
86    fn annotations(&self) -> Result<StoreIdIterator> {
87        self.links()
88            .map(|it| {
89                it.filter(|link| link.get_store_id().is_in_collection(&["annotation"]))
90                    .map(|link| Ok(link.get_store_id().clone()))
91            })
92            .map(Box::new)
93            .map(|inner| StoreIdIterator::new(inner))
94    }
95
96    fn is_annotation(&self) -> Result<bool> {
97        self.is::<IsAnnotation>()
98    }
99
100}
101