imag/lib/entry/libimagentrylink/src/linkable.rs

396 lines
14 KiB
Rust
Raw Normal View History

//
// 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 std::path::PathBuf;
use libimagstore::storeid::StoreId;
use libimagstore::store::Entry;
2018-02-25 16:04:05 +00:00
use libimagstore::store::Store;
2016-02-03 14:47:14 +00:00
use toml_query::read::Partial;
2017-08-26 15:53:08 +00:00
use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::err_msg;
2017-08-26 15:53:08 +00:00
use crate::iter::LinkIter;
use crate::link::Link;
2016-02-03 14:47:14 +00:00
pub trait Linkable {
2016-02-03 14:47:14 +00:00
/// Get all links
fn links(&self) -> Result<LinkIter>;
/// Add an internal link to the implementor object
fn add_link(&mut self, link: &mut Entry) -> Result<()>;
/// Remove an internal link from the implementor object
fn remove_link(&mut self, link: &mut Entry) -> Result<()>;
2016-02-03 14:47:14 +00:00
2018-02-25 16:04:05 +00:00
/// Remove _all_ internal links
fn unlink(&mut self, store: &Store) -> Result<()>;
}
#[derive(Serialize, Deserialize, Debug)]
struct LinkPartial {
internal: Option<Vec<String>>,
}
impl Default for LinkPartial {
fn default() -> Self {
LinkPartial {
internal: None,
}
}
}
impl<'a> Partial<'a> for LinkPartial {
const LOCATION: &'static str = "links";
type Output = Self;
2016-02-03 14:47:14 +00:00
}
impl Linkable for Entry {
fn links(&self) -> Result<LinkIter> {
2018-04-30 13:48:08 +00:00
debug!("Getting internal links");
trace!("Getting internal links from header of '{}' = {:?}", self.get_location(), self.get_header());
let partial : LinkPartial = self
2017-08-26 15:53:08 +00:00
.get_header()
.read_partial::<LinkPartial>()?
.unwrap_or_else(|| LinkPartial::default());
partial
.internal
.unwrap_or_else(|| vec![])
.into_iter()
.map(PathBuf::from)
.map(StoreId::new)
.map(|r| r.map(Link::from))
.collect::<Result<Vec<Link>>>()
.map(LinkIter::new)
}
fn add_link(&mut self, other: &mut Entry) -> Result<()> {
debug!("Adding internal link: {:?}", other);
let left_location = self.get_location().to_str()?;
let right_location = other.get_location().to_str()?;
alter_linking(self, other, |mut left, mut right| {
let mut left_internal = left.internal.unwrap_or_else(|| vec![]);
left_internal.push(right_location);
left_internal.sort_unstable();
left_internal.dedup();
let mut right_internal = right.internal.unwrap_or_else(|| vec![]);
right_internal.push(left_location);
right_internal.sort_unstable();
right_internal.dedup();
left.internal = Some(left_internal);
right.internal = Some(right_internal);
2016-10-15 09:24:14 +00:00
Ok((left, right))
})
}
fn remove_link(&mut self, other: &mut Entry) -> Result<()> {
debug!("Remove internal link: {:?}", other);
let left_location = self.get_location().to_str()?;
let right_location = other.get_location().to_str()?;
alter_linking(self, other, |mut left, mut right| {
let mut left_internal = left.internal.unwrap_or_else(|| vec![]);
left_internal.retain(|l| *l != right_location);
left_internal.sort_unstable();
left_internal.dedup();
let mut right_internal = right.internal.unwrap_or_else(|| vec![]);
right_internal.retain(|l| *l != left_location);
right_internal.sort_unstable();
right_internal.dedup();
left.internal = Some(left_internal);
right.internal = Some(right_internal);
Ok((left, right))
})
}
2018-02-25 16:04:05 +00:00
fn unlink(&mut self, store: &Store) -> Result<()> {
for id in self.links()?.map(|l| l.get_store_id().clone()) {
match store.get(id).context("Failed to get entry")? {
Some(mut entry) => self.remove_link(&mut entry)?,
None => return Err(err_msg("Link target does not exist")),
2018-02-25 16:04:05 +00:00
}
}
Ok(())
}
}
fn alter_linking<F>(left: &mut Entry, right: &mut Entry, f: F) -> Result<()>
where F: FnOnce(LinkPartial, LinkPartial) -> Result<(LinkPartial, LinkPartial)>
{
debug!("Altering linkage of {:?} and {:?}", left, right);
2016-02-15 12:19:34 +00:00
let get_partial = |entry: &mut Entry| -> Result<LinkPartial> {
Ok(entry.get_header().read_partial::<LinkPartial>()?.unwrap_or_else(|| LinkPartial::default()))
};
2016-04-08 22:07:30 +00:00
let left_partial : LinkPartial = get_partial(left)?;
let right_partial : LinkPartial = get_partial(right)?;
trace!("Partial left before: {:?}", left_partial);
trace!("Partial right before: {:?}", right_partial);
let (left_partial, right_partial) = f(left_partial, right_partial)?;
trace!("Partial left after: {:?}", left_partial);
trace!("Partial right after: {:?}", right_partial);
2016-02-14 16:50:06 +00:00
left.get_header_mut().insert_serialized("links", left_partial)?;
right.get_header_mut().insert_serialized("links", right_partial)?;
2016-02-14 16:50:06 +00:00
debug!("Finished altering linkage!");
Ok(())
2016-02-14 16:50:06 +00:00
}
2016-10-15 08:33:30 +00:00
#[cfg(test)]
mod test {
use std::path::PathBuf;
use libimagstore::store::Store;
use super::Linkable;
2016-10-15 08:33:30 +00:00
2016-10-15 08:58:50 +00:00
fn setup_logging() {
2017-12-07 21:07:01 +00:00
let _ = ::env_logger::try_init();
2016-10-15 08:58:50 +00:00
}
2016-10-15 08:33:30 +00:00
pub fn get_store() -> Store {
Store::new_inmemory(PathBuf::from("/"), &None).unwrap()
2016-10-15 08:33:30 +00:00
}
#[test]
fn test_new_entry_no_links() {
2016-10-15 08:58:50 +00:00
setup_logging();
2016-10-15 08:33:30 +00:00
let store = get_store();
let entry = store.create(PathBuf::from("test_new_entry_no_links")).unwrap();
let links = entry.links();
2016-10-15 08:33:30 +00:00
assert!(links.is_ok());
let links = links.unwrap();
assert_eq!(links.collect::<Vec<_>>().len(), 0);
}
2016-10-15 08:41:13 +00:00
#[test]
fn test_link_two_entries() {
2016-10-15 08:58:50 +00:00
setup_logging();
2016-10-15 08:41:13 +00:00
let store = get_store();
let mut e1 = store.create(PathBuf::from("test_link_two_entries1")).unwrap();
assert!(e1.links().is_ok());
2016-10-15 08:41:13 +00:00
let mut e2 = store.create(PathBuf::from("test_link_two_entries2")).unwrap();
assert!(e2.links().is_ok());
2016-10-15 08:41:13 +00:00
{
let res = e1.add_link(&mut e2);
debug!("Result = {:?}", res);
assert!(res.is_ok());
2016-10-15 08:41:13 +00:00
let e1_links = e1.links().unwrap().collect::<Vec<_>>();
let e2_links = e2.links().unwrap().collect::<Vec<_>>();
2016-10-15 08:41:13 +00:00
2016-10-15 09:24:14 +00:00
debug!("1 has links: {:?}", e1_links);
debug!("2 has links: {:?}", e2_links);
2016-10-15 08:41:13 +00:00
assert_eq!(e1_links.len(), 1);
assert_eq!(e2_links.len(), 1);
assert!(e1_links.first().map(|l| l.clone().eq_store_id(e2.get_location())).unwrap_or(false));
assert!(e2_links.first().map(|l| l.clone().eq_store_id(e1.get_location())).unwrap_or(false));
2016-10-15 08:41:13 +00:00
}
{
assert!(e1.remove_link(&mut e2).is_ok());
2016-10-15 08:41:13 +00:00
2018-11-01 10:51:07 +00:00
debug!("{:?}", e2.to_str());
let e2_links = e2.links().unwrap().collect::<Vec<_>>();
2016-10-15 08:41:13 +00:00
assert_eq!(e2_links.len(), 0, "Expected [], got: {:?}", e2_links);
2018-11-01 10:51:07 +00:00
debug!("{:?}", e1.to_str());
let e1_links = e1.links().unwrap().collect::<Vec<_>>();
2016-10-15 08:41:13 +00:00
assert_eq!(e1_links.len(), 0, "Expected [], got: {:?}", e1_links);
}
}
2016-10-15 09:31:30 +00:00
#[test]
fn test_multiple_links() {
setup_logging();
let store = get_store();
let mut e1 = store.retrieve(PathBuf::from("1")).unwrap();
let mut e2 = store.retrieve(PathBuf::from("2")).unwrap();
let mut e3 = store.retrieve(PathBuf::from("3")).unwrap();
let mut e4 = store.retrieve(PathBuf::from("4")).unwrap();
let mut e5 = store.retrieve(PathBuf::from("5")).unwrap();
assert!(e1.add_link(&mut e2).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
assert!(e1.add_link(&mut e3).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 2);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
assert!(e1.add_link(&mut e4).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 3);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
assert!(e1.add_link(&mut e5).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 4);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 1);
2016-10-15 09:31:30 +00:00
assert!(e5.remove_link(&mut e1).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 3);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
assert!(e4.remove_link(&mut e1).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 2);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
assert!(e3.remove_link(&mut e1).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
assert!(e2.remove_link(&mut e1).is_ok());
2016-10-15 09:31:30 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e4.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e5.links().unwrap().collect::<Vec<_>>().len(), 0);
2016-10-15 09:31:30 +00:00
}
2017-07-14 21:17:32 +00:00
#[test]
fn test_link_deleting() {
setup_logging();
let store = get_store();
let mut e1 = store.retrieve(PathBuf::from("1")).unwrap();
let mut e2 = store.retrieve(PathBuf::from("2")).unwrap();
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 0);
2017-07-14 21:17:32 +00:00
assert!(e1.add_link(&mut e2).is_ok());
2017-07-14 21:17:32 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
2017-07-14 21:17:32 +00:00
assert!(e1.remove_link(&mut e2).is_ok());
2017-07-14 21:17:32 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 0);
2017-07-14 21:17:32 +00:00
}
2017-07-14 21:21:38 +00:00
#[test]
fn test_link_deleting_multiple_links() {
setup_logging();
let store = get_store();
let mut e1 = store.retrieve(PathBuf::from("1")).unwrap();
let mut e2 = store.retrieve(PathBuf::from("2")).unwrap();
let mut e3 = store.retrieve(PathBuf::from("3")).unwrap();
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 0);
2017-07-14 21:21:38 +00:00
assert!(e1.add_link(&mut e2).is_ok()); // 1-2
assert!(e1.add_link(&mut e3).is_ok()); // 1-2, 1-3
2017-07-14 21:21:38 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 2);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
2017-07-14 21:21:38 +00:00
assert!(e2.add_link(&mut e3).is_ok()); // 1-2, 1-3, 2-3
2017-07-14 21:21:38 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 2);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 2);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 2);
2017-07-14 21:21:38 +00:00
assert!(e1.remove_link(&mut e2).is_ok()); // 1-3, 2-3
2017-07-14 21:21:38 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 2);
2017-07-14 21:21:38 +00:00
assert!(e1.remove_link(&mut e3).is_ok()); // 2-3
2017-07-14 21:21:38 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 1);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 1);
2017-07-14 21:21:38 +00:00
assert!(e2.remove_link(&mut e3).is_ok());
2017-07-14 21:21:38 +00:00
assert_eq!(e1.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e2.links().unwrap().collect::<Vec<_>>().len(), 0);
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 0);
2017-07-14 21:21:38 +00:00
}
2016-10-15 08:33:30 +00:00
}