summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2019-06-21 22:30:03 +0200
committerMatthias Beyer <mail@beyermatthias.de>2019-06-26 20:26:41 +0200
commit0eac6d9931577da49e3e546a8484ec039d41506e (patch)
tree9ddb8c42032b4d12ac41fc40b2a3b62edd64f159
parent56f8aecdbfd656b32f8a434ee978ac5a61a3c018 (diff)
downloadimag-0eac6d9931577da49e3e546a8484ec039d41506e.tar.gz
imag-0eac6d9931577da49e3e546a8484ec039d41506e.tar.xz
-rw-r--r--lib/entry/libimagentrylink/src/link.rs31
-rw-r--r--lib/entry/libimagentrylink/src/linkable.rs167
2 files changed, 193 insertions, 5 deletions
diff --git a/lib/entry/libimagentrylink/src/link.rs b/lib/entry/libimagentrylink/src/link.rs
index 82370111..b60e22ca 100644
--- a/lib/entry/libimagentrylink/src/link.rs
+++ b/lib/entry/libimagentrylink/src/link.rs
@@ -30,6 +30,8 @@ use failure::Error;
#[derive(Eq, PartialOrd, Ord, Hash, Debug, Clone)]
pub enum Link {
Id { link: StoreId },
+ LinkTo { link: StoreId },
+ LinkFrom { link: StoreId },
}
impl Link {
@@ -37,6 +39,8 @@ impl Link {
pub fn exists(&self, store: &Store) -> Result<bool> {
match *self {
Link::Id { ref link } => store.exists(link.clone()),
+ Link::LinkTo { ref link } => store.exists(link.clone()),
+ Link::LinkFrom { ref link } => store.exists(link.clone()),
}
.map_err(From::from)
}
@@ -44,6 +48,8 @@ impl Link {
pub fn to_str(&self) -> Result<String> {
match *self {
Link::Id { ref link } => link.to_str(),
+ Link::LinkTo { ref link } => link.to_str(),
+ Link::LinkFrom { ref link } => link.to_str(),
}
.map_err(From::from)
}
@@ -52,6 +58,8 @@ impl Link {
pub(crate) fn eq_store_id(&self, id: &StoreId) -> bool {
match self {
&Link::Id { link: ref s } => s.eq(id),
+ &Link::LinkTo { ref link } => link.eq(id),
+ &Link::LinkFrom { ref link } => link.eq(id),
}
}
@@ -59,17 +67,21 @@ impl Link {
pub fn get_store_id(&self) -> &StoreId {
match self {
&Link::Id { link: ref s } => s,
+ &Link::LinkTo { ref link } => link,
+ &Link::LinkFrom { ref link } => link,
}
}
pub(crate) fn to_value(&self) -> Result<Value> {
match self {
- Link::Id { ref link } => link
- .to_str()
- .map(Value::String)
- .context(EM::ConversionError)
- .map_err(Error::from),
+ Link::Id { ref link } => link,
+ Link::LinkTo { ref link } => link,
+ Link::LinkFrom { ref link } => link,
}
+ .to_str()
+ .map(Value::String)
+ .context(EM::ConversionError)
+ .map_err(Error::from)
}
}
@@ -78,6 +90,9 @@ impl ::std::cmp::PartialEq for Link {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&Link::Id { link: ref a }, &Link::Id { link: ref b }) => a.eq(&b),
+ (&Link::LinkTo { link: ref a }, &Link::LinkTo { link: ref b })=> a.eq(&b),
+ (&Link::LinkFrom { link: ref a }, &Link::LinkFrom { link: ref b })=> a.eq(&b),
+ _ => false,
}
}
}
@@ -93,6 +108,8 @@ impl Into<StoreId> for Link {
fn into(self) -> StoreId {
match self {
Link::Id { link } => link,
+ Link::LinkTo { link } => link,
+ Link::LinkFrom { link } => link,
}
}
}
@@ -101,6 +118,8 @@ impl IntoStoreId for Link {
fn into_storeid(self) -> Result<StoreId> {
match self {
Link::Id { link } => Ok(link),
+ Link::LinkTo { link } => Ok(link),
+ Link::LinkFrom { link } => Ok(link),
}
}
}
@@ -109,6 +128,8 @@ impl AsRef<StoreId> for Link {
fn as_ref(&self) -> &StoreId {
match self {
&Link::Id { ref link } => &link,
+ &Link::LinkTo { ref link } => &link,
+ &Link::LinkFrom { ref link } => &link,
}
}
}
diff --git a/lib/entry/libimagentrylink/src/linkable.rs b/lib/entry/libimagentrylink/src/linkable.rs
index 104e6421..2c07af4b 100644
--- a/lib/entry/libimagentrylink/src/linkable.rs
+++ b/lib/entry/libimagentrylink/src/linkable.rs
@@ -38,6 +38,15 @@ pub trait Linkable {
/// Get all links
fn links(&self) -> Result<LinkIter>;
+ /// Get all links which are unidirectional links
+ fn unidirectional_links(&self) -> Result<LinkIter>;
+
+ /// Get all links which are directional links, outgoing
+ fn directional_links_to(&self) -> Result<LinkIter>;
+
+ /// Get all links which are directional links, incoming
+ fn directional_links_from(&self) -> Result<LinkIter>;
+
/// Add an internal link to the implementor object
fn add_link(&mut self, link: &mut Entry) -> Result<()>;
@@ -47,17 +56,27 @@ pub trait Linkable {
/// Remove _all_ internal links
fn unlink(&mut self, store: &Store) -> Result<()>;
+ /// Add a directional link: self -> otehr
+ fn add_link_to(&mut self, other: &mut Entry) -> Result<()>;
+
+ /// Remove a directional link: self -> otehr
+ fn remove_link_to(&mut self, other: &mut Entry) -> Result<()>;
+
}
#[derive(Serialize, Deserialize, Debug)]
struct LinkPartial {
internal: Option<Vec<String>>,
+ from: Option<Vec<String>>,
+ to: Option<Vec<String>>,
}
impl Default for LinkPartial {
fn default() -> Self {
LinkPartial {
internal: None,
+ from: None,
+ to: None,
}
}
}
@@ -82,6 +101,8 @@ impl Linkable for Entry {
.internal
.unwrap_or_else(|| vec![])
.into_iter()
+ .chain(partial.from.unwrap_or_else(|| vec![]).into_iter())
+ .chain(partial.to.unwrap_or_else(|| vec![]).into_iter())
.map(PathBuf::from)
.map(StoreId::new)
.map(|r| r.map(Link::from))
@@ -89,6 +110,48 @@ impl Linkable for Entry {
.map(LinkIter::new)
}
+ /// Get all links which are unidirectional links
+ fn unidirectional_links(&self) -> Result<LinkIter> {
+ trace!("Getting unidirectional links from header of '{}' = {:?}", self.get_location(), self.get_header());
+
+ let iter = self.get_header()
+ .read_partial::<LinkPartial>()?
+ .unwrap_or_else(Default::default)
+ .internal
+ .unwrap_or_else(|| vec![])
+ .into_iter();
+
+ link_string_iter_to_link_iter(iter)
+ }
+
+ /// Get all links which are directional links, outgoing
+ fn directional_links_to(&self) -> Result<LinkIter> {
+ trace!("Getting unidirectional (to) links from header of '{}' = {:?}", self.get_location(), self.get_header());
+
+ let iter = self.get_header()
+ .read_partial::<LinkPartial>()?
+ .unwrap_or_else(Default::default)
+ .to
+ .unwrap_or_else(|| vec![])
+ .into_iter();
+
+ link_string_iter_to_link_iter(iter)
+ }
+
+ /// Get all links which are directional links, incoming
+ fn directional_links_from(&self) -> Result<LinkIter> {
+ trace!("Getting unidirectional (from) links from header of '{}' = {:?}", self.get_location(), self.get_header());
+
+ let iter = self.get_header()
+ .read_partial::<LinkPartial>()?
+ .unwrap_or_else(Default::default)
+ .from
+ .unwrap_or_else(|| vec![])
+ .into_iter();
+
+ link_string_iter_to_link_iter(iter)
+ }
+
fn add_link(&mut self, other: &mut Entry) -> Result<()> {
debug!("Adding internal link: {:?}", other);
let left_location = self.get_location().to_str()?;
@@ -150,6 +213,53 @@ impl Linkable for Entry {
Ok(())
}
+ fn add_link_to(&mut self, other: &mut Entry) -> Result<()> {
+ 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_to = left.to.unwrap_or_else(|| vec![]);
+ left_to.push(right_location);
+
+ let mut right_from = right.from.unwrap_or_else(|| vec![]);
+ right_from.push(left_location);
+
+ left.to = Some(left_to);
+ right.from = Some(right_from);
+
+ Ok((left, right))
+ })
+ }
+
+ /// Remove a directional link: self -> otehr
+ fn remove_link_to(&mut self, other: &mut Entry) -> Result<()> {
+ 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_to = left.to.unwrap_or_else(|| vec![]);
+ left_to.retain(|l| *l != right_location);
+
+ let mut right_from = right.from.unwrap_or_else(|| vec![]);
+ right_from.retain(|l| *l != left_location);
+
+ left.to = Some(left_to);
+ right.from = Some(right_from);
+
+ Ok((left, right))
+ })
+ }
+
+}
+
+fn link_string_iter_to_link_iter<I>(iter: I) -> Result<LinkIter>
+ where I: Iterator<Item = String>
+{
+ iter.map(PathBuf::from)
+ .map(StoreId::new)
+ .map(|r| r.map(Link::from))
+ .collect::<Result<Vec<Link>>>()
+ .map(LinkIter::new)
}
fn alter_linking<F>(left: &mut Entry, right: &mut Entry, f: F) -> Result<()>
@@ -392,4 +502,61 @@ mod test {
assert_eq!(e3.links().unwrap().collect::<Vec<_>>().len(), 0);
}
+ #[test]
+ fn test_directional_link() {
+ use libimagstore::store::Entry;
+
+ setup_logging();
+ let store = get_store();
+ let mut entry1 = store.create(PathBuf::from("test_directional_link-1")).unwrap();
+ let mut entry2 = store.create(PathBuf::from("test_directional_link-2")).unwrap();
+
+ assert!(entry1.unidirectional_links().unwrap().collect::<Vec<_>>().is_empty());
+ assert!(entry2.unidirectional_links().unwrap().collect::<Vec<_>>().is_empty());
+
+ assert!(entry1.directional_links_to().unwrap().collect::<Vec<_>>().is_empty());
+ assert!(entry2.directional_links_to().unwrap().collect::<Vec<_>>().is_empty());
+
+ assert!(entry1.directional_links_from().unwrap().collect::<Vec<_>>().is_empty());
+ assert!(entry2.directional_links_from().unwrap().collect::<Vec<_>>().is_empty());
+
+ assert!(entry1.add_link_to(&mut entry2).is_ok());
+
+ assert_eq!(entry1.unidirectional_links().unwrap().collect::<Vec<_>>(), vec![]);
+ assert_eq!(entry2.unidirectional_links().unwrap().collect::<Vec<_>>(), vec![]);
+
+ let get_directional_links_to = |e: &Entry| -> Result<Vec<String>, _> {
+ e.directional_links_to()
+ .unwrap()
+ .map(|l| l.to_str())
+ .collect::<Result<Vec<_>, _>>()
+ };
+
+ let get_directional_links_from = |e: &Entry| {
+ e.directional_links_from()
+ .unwrap()
+ .map(|l| l.to_str())
+ .collect::<Result<Vec<_>, _>>()
+ };
+
+ {
+ let entry1_dir_links = get_directional_links_to(&entry1).unwrap();
+ assert_eq!(entry1_dir_links, vec!["test_directional_link-2"]);
+ }
+ {
+ let entry2_dir_links = get_directional_links_to(&entry2).unwrap();
+ assert!(entry2_dir_links.is_empty());
+ }
+
+ {
+ let entry1_dir_links = get_directional_links_from(&entry1).unwrap();
+ assert!(entry1_dir_links.is_empty());
+ }
+ {
+ let entry2_dir_links = get_directional_links_from(&entry2).unwrap();
+ assert_eq!(entry2_dir_links, vec!["test_directional_link-1"]);
+ }
+
+ }
+
}