Skip to main content

libimagentrylink/
linkable.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 std::path::PathBuf;
21
22use libimagstore::storeid::StoreId;
23use libimagstore::store::Entry;
24use libimagstore::store::Store;
25
26use toml_query::read::Partial;
27use toml_query::read::TomlValueReadExt;
28use toml_query::insert::TomlValueInsertExt;
29use failure::ResultExt;
30use failure::Fallible as Result;
31use failure::err_msg;
32
33use crate::iter::LinkIter;
34use crate::link::Link;
35
36pub trait Linkable {
37
38    /// Get all links
39    fn links(&self) -> Result<LinkIter>;
40
41    /// Get all links which are unidirectional links
42    fn unidirectional_links(&self) -> Result<LinkIter>;
43
44    /// Get all links which are directional links, outgoing
45    fn directional_links_to(&self) -> Result<LinkIter>;
46
47    /// Get all links which are directional links, incoming
48    fn directional_links_from(&self) -> Result<LinkIter>;
49
50    /// Add an internal link to the implementor object
51    fn add_link(&mut self, link: &mut Entry) -> Result<()>;
52
53    /// Remove an internal link from the implementor object
54    fn remove_link(&mut self, link: &mut Entry) -> Result<()>;
55
56    /// Remove _all_ internal links
57    fn unlink(&mut self, store: &Store) -> Result<()>;
58
59    /// Add a directional link: self -> otehr
60    fn add_link_to(&mut self, other: &mut Entry) -> Result<()>;
61
62    /// Remove a directional link: self -> otehr
63    fn remove_link_to(&mut self, other: &mut Entry) -> Result<()>;
64
65    /// Check whether an entry is linked to another entry
66    fn is_linked_to(&self, other: &Entry) -> Result<bool>;
67}
68
69#[derive(Serialize, Deserialize, Debug)]
70struct LinkPartial {
71    internal: Option<Vec<String>>,
72    from: Option<Vec<String>>,
73    to: Option<Vec<String>>,
74}
75
76impl Default for LinkPartial {
77    fn default() -> Self {
78        LinkPartial {
79            internal: None,
80            from: None,
81            to: None,
82        }
83    }
84}
85
86impl<'a> Partial<'a> for LinkPartial {
87    const LOCATION: &'static str = "links";
88    type Output = Self;
89}
90
91impl Linkable for Entry {
92
93    fn links(&self) -> Result<LinkIter> {
94        debug!("Getting internal links");
95        trace!("Getting internal links from header of '{}' = {:?}", self.get_location(), self.get_header());
96
97        let partial : LinkPartial = self
98            .get_header()
99            .read_partial::<LinkPartial>()?
100            .unwrap_or_else(LinkPartial::default);
101
102        partial
103            .internal
104            .unwrap_or_else(|| vec![])
105            .into_iter()
106            .chain(partial.from.unwrap_or_else(|| vec![]).into_iter())
107            .chain(partial.to.unwrap_or_else(|| vec![]).into_iter())
108            .map(PathBuf::from)
109            .map(StoreId::new)
110            .map(|r| r.map(Link::from))
111            .collect::<Result<Vec<Link>>>()
112            .map(LinkIter::new)
113    }
114
115    /// Get all links which are unidirectional links
116    fn unidirectional_links(&self) -> Result<LinkIter> {
117        debug!("Getting unidirectional links");
118        trace!("Getting unidirectional links from header of '{}' = {:?}", self.get_location(), self.get_header());
119
120        let iter = self.get_header()
121            .read_partial::<LinkPartial>()?
122            .unwrap_or_else(Default::default)
123            .internal
124            .unwrap_or_else(|| vec![])
125            .into_iter();
126
127        link_string_iter_to_link_iter(iter)
128    }
129
130    /// Get all links which are directional links, outgoing
131    fn directional_links_to(&self) -> Result<LinkIter> {
132        debug!("Getting directional links (to)");
133        trace!("Getting unidirectional (to) links from header of '{}' = {:?}", self.get_location(), self.get_header());
134
135        let iter = self.get_header()
136            .read_partial::<LinkPartial>()?
137            .unwrap_or_else(Default::default)
138            .to
139            .unwrap_or_else(|| vec![])
140            .into_iter();
141
142        link_string_iter_to_link_iter(iter)
143    }
144
145    /// Get all links which are directional links, incoming
146    fn directional_links_from(&self) -> Result<LinkIter> {
147        debug!("Getting directional links (from)");
148        trace!("Getting unidirectional (from) links from header of '{}' = {:?}", self.get_location(), self.get_header());
149
150        let iter = self.get_header()
151            .read_partial::<LinkPartial>()?
152            .unwrap_or_else(Default::default)
153            .from
154            .unwrap_or_else(|| vec![])
155            .into_iter();
156
157        link_string_iter_to_link_iter(iter)
158    }
159
160    fn add_link(&mut self, other: &mut Entry) -> Result<()> {
161        debug!("Adding internal link: {:?}", other);
162        let left_location  = self.get_location().to_str()?;
163        let right_location = other.get_location().to_str()?;
164
165        alter_linking(self, other, |mut left, mut right| {
166            let mut left_internal = left.internal.unwrap_or_else(|| vec![]);
167            trace!("left: {:?} <- {:?}", left_internal, right_location);
168            left_internal.push(right_location);
169            trace!("left: {:?}", left_internal);
170
171            left_internal.sort_unstable();
172            left_internal.dedup();
173
174            let mut right_internal = right.internal.unwrap_or_else(|| vec![]);
175            trace!("right: {:?} <- {:?}", right_internal, left_location);
176            right_internal.push(left_location);
177            trace!("right: {:?}", right_internal);
178
179            right_internal.sort_unstable();
180            right_internal.dedup();
181
182            left.internal = Some(left_internal);
183            right.internal = Some(right_internal);
184
185            trace!("Finished: ({:?}, {:?})", left, right);
186            Ok((left, right))
187        })
188    }
189
190    fn remove_link(&mut self, other: &mut Entry) -> Result<()> {
191        debug!("Remove internal link: {:?}", other);
192        let left_location  = self.get_location().to_str()?;
193        let right_location = other.get_location().to_str()?;
194
195        alter_linking(self, other, |mut left, mut right| {
196            let mut left_internal = left.internal.unwrap_or_else(|| vec![]);
197            trace!("left: {:?} retaining {:?}", left_internal, right_location);
198            left_internal.retain(|l| *l != right_location);
199            trace!("left: {:?}", left_internal);
200
201            left_internal.sort_unstable();
202            left_internal.dedup();
203
204            let mut right_internal = right.internal.unwrap_or_else(|| vec![]);
205            trace!("right: {:?} retaining {:?}", right_internal, left_location);
206            right_internal.retain(|l| *l != left_location);
207            trace!("right: {:?}", right_internal);
208
209            right_internal.sort_unstable();
210            right_internal.dedup();
211
212            left.internal = Some(left_internal);
213            right.internal = Some(right_internal);
214
215            trace!("Finished: ({:?}, {:?})", left, right);
216            Ok((left, right))
217        })
218    }
219
220    fn unlink(&mut self, store: &Store) -> Result<()> {
221        debug!("Unlinking {:?}", self);
222        for id in self.links()?.map(|l| l.get_store_id().clone()) {
223            match store.get(id).context("Failed to get entry")? {
224                Some(mut entry) => self.remove_link(&mut entry)?,
225                None            => return Err(err_msg("Link target does not exist")),
226            }
227        }
228
229        Ok(())
230    }
231
232    fn add_link_to(&mut self, other: &mut Entry) -> Result<()> {
233        let left_location  = self.get_location().to_str()?;
234        let right_location = other.get_location().to_str()?;
235
236        alter_linking(self, other, |mut left, mut right| {
237            let mut left_to = left.to.unwrap_or_else(|| vec![]);
238            trace!("left_to: {:?} <- {:?}", left_to, right_location);
239            left_to.push(right_location);
240            trace!("left_to: {:?}", left_to);
241
242            let mut right_from = right.from.unwrap_or_else(|| vec![]);
243            trace!("right_from: {:?} <- {:?}", right_from, left_location);
244            right_from.push(left_location);
245            trace!("right_from: {:?}", right_from);
246
247            left.to = Some(left_to);
248            right.from = Some(right_from);
249
250            trace!("Finished: ({:?}, {:?})", left, right);
251            Ok((left, right))
252        })
253    }
254
255    /// Remove a directional link: self -> otehr
256    fn remove_link_to(&mut self, other: &mut Entry) -> Result<()> {
257        let left_location  = self.get_location().to_str()?;
258        let right_location = other.get_location().to_str()?;
259
260        alter_linking(self, other, |mut left, mut right| {
261            let mut left_to = left.to.unwrap_or_else(|| vec![]);
262            trace!("left_to: {:?} retaining {:?}", left_to, right_location);
263            left_to.retain(|l| *l != right_location);
264            trace!("left_to: {:?}", left_to);
265
266            let mut right_from = right.from.unwrap_or_else(|| vec![]);
267            trace!("right_from: {:?} retaining {:?}", right_from, left_location);
268            right_from.retain(|l| *l != left_location);
269            trace!("right_from: {:?}", right_from);
270
271            left.to = Some(left_to);
272            right.from = Some(right_from);
273
274            trace!("Finished: ({:?}, {:?})", left, right);
275            Ok((left, right))
276        })
277    }
278
279    /// Check whether an entry is linked to another entry
280    fn is_linked_to(&self, other: &Entry) -> Result<bool> {
281        let left_partial  = get_link_partial(self)?
282            .ok_or_else(|| format_err!("Cannot read links from {}", self.get_location()))?;
283        let right_partial = get_link_partial(&other)?
284            .ok_or_else(|| format_err!("Cannot read links from {}", other.get_location()))?;
285
286        let left_id       = self.get_location();
287        let right_id      = other.get_location();
288
289        let strary_contains = |sary: &Vec<String>, id: &StoreId| -> Result<bool> {
290            sary.iter().map(|e| {
291                StoreId::new(PathBuf::from(e)).map(|e| e == *id)
292            }).fold(Ok(false), |a, e| a.and_then(|_| e))
293        };
294
295        let is_linked_from = |partial: &LinkPartial, id| {
296            partial.from.as_ref().map(|f| strary_contains(f, id)).unwrap_or(Ok(false))
297        };
298        let is_linked_to   = |partial: &LinkPartial, id| {
299            partial.to.as_ref().map(|t| strary_contains(t, id)).unwrap_or(Ok(false))
300        };
301
302        Ok({
303            is_linked_from(&left_partial, &right_id)? && is_linked_from(&right_partial, &left_id)?
304            ||
305            is_linked_to(&left_partial, &right_id)? && is_linked_to(&right_partial, &left_id)?
306        })
307    }
308}
309
310fn link_string_iter_to_link_iter<I>(iter: I) -> Result<LinkIter>
311    where I: Iterator<Item = String>
312{
313    iter.map(PathBuf::from)
314        .map(StoreId::new)
315        .map(|r| r.map(Link::from))
316        .collect::<Result<Vec<Link>>>()
317        .map(LinkIter::new)
318}
319
320fn alter_linking<F>(left: &mut Entry, right: &mut Entry, f: F) -> Result<()>
321    where F: FnOnce(LinkPartial, LinkPartial) -> Result<(LinkPartial, LinkPartial)>
322{
323    debug!("Altering linkage of {:?} and {:?}", left, right);
324    let get_partial = |e| -> Result<_> {
325        Ok(get_link_partial(e)?.unwrap_or_else(LinkPartial::default))
326    };
327
328    let left_partial : LinkPartial = get_partial(&left)?;
329    let right_partial : LinkPartial = get_partial(&right)?;
330
331    trace!("Partial left before: {:?}", left_partial);
332    trace!("Partial right before: {:?}", right_partial);
333
334    let (left_partial, right_partial) = f(left_partial, right_partial)?;
335
336    trace!("Partial left after: {:?}", left_partial);
337    trace!("Partial right after: {:?}", right_partial);
338
339    left.get_header_mut().insert_serialized("links", left_partial)?;
340    right.get_header_mut().insert_serialized("links", right_partial)?;
341
342    debug!("Finished altering linkage!");
343    Ok(())
344}
345
346fn get_link_partial(entry: &Entry) -> Result<Option<LinkPartial>> {
347    use failure::Error;
348    entry.get_header().read_partial::<LinkPartial>().map_err(Error::from)
349}
350
351
352#[cfg(test)]
353mod test {
354    use std::path::PathBuf;
355
356    use libimagstore::store::Store;
357
358    use super::Linkable;
359
360    fn setup_logging() {
361        let _ = ::env_logger::try_init();
362    }
363
364    pub fn get_store() -> Store {
365        Store::new_inmemory(PathBuf::from("/"), &None).unwrap()
366    }
367
368    #[test]
369    fn test_new_entry_no_links() {
370        setup_logging();
371        let store = get_store();
372        let entry = store.create(PathBuf::from("test_new_entry_no_links")).unwrap();
373        let links = entry.links();
374        assert!(links.is_ok());
375        let links = links.unwrap();
376        assert_eq!(links.count(), 0);
377    }
378
379    #[test]
380    fn test_link_two_entries() {
381        setup_logging();
382        let store = get_store();
383        let mut e1 = store.create(PathBuf::from("test_link_two_entries1")).unwrap();
384        assert!(e1.links().is_ok());
385
386        let mut e2 = store.create(PathBuf::from("test_link_two_entries2")).unwrap();
387        assert!(e2.links().is_ok());
388
389        {
390            let res = e1.add_link(&mut e2);
391            debug!("Result = {:?}", res);
392            assert!(res.is_ok());
393
394            let e1_links = e1.links().unwrap().collect::<Vec<_>>();
395            let e2_links = e2.links().unwrap().collect::<Vec<_>>();
396
397            debug!("1 has links: {:?}", e1_links);
398            debug!("2 has links: {:?}", e2_links);
399
400            assert_eq!(e1_links.len(), 1);
401            assert_eq!(e2_links.len(), 1);
402
403            assert!(e1_links.first().map(|l| l.clone().eq_store_id(e2.get_location())).unwrap_or(false));
404            assert!(e2_links.first().map(|l| l.clone().eq_store_id(e1.get_location())).unwrap_or(false));
405        }
406
407        {
408            assert!(e1.remove_link(&mut e2).is_ok());
409
410            debug!("{:?}", e2.to_str());
411            let e2_links = e2.links().unwrap().collect::<Vec<_>>();
412            assert_eq!(e2_links.len(), 0, "Expected [], got: {:?}", e2_links);
413
414            debug!("{:?}", e1.to_str());
415            let e1_links = e1.links().unwrap().collect::<Vec<_>>();
416            assert_eq!(e1_links.len(), 0, "Expected [], got: {:?}", e1_links);
417
418        }
419    }
420
421    #[test]
422    #[clippy::cognitive_complexity = "49"]
423    fn test_multiple_links() {
424        setup_logging();
425        let store = get_store();
426
427        let mut e1 = store.retrieve(PathBuf::from("1")).unwrap();
428        let mut e2 = store.retrieve(PathBuf::from("2")).unwrap();
429        let mut e3 = store.retrieve(PathBuf::from("3")).unwrap();
430        let mut e4 = store.retrieve(PathBuf::from("4")).unwrap();
431        let mut e5 = store.retrieve(PathBuf::from("5")).unwrap();
432
433        assert!(e1.add_link(&mut e2).is_ok());
434
435        assert_eq!(e1.links().unwrap().count(), 1);
436        assert_eq!(e2.links().unwrap().count(), 1);
437        assert_eq!(e3.links().unwrap().count(), 0);
438        assert_eq!(e4.links().unwrap().count(), 0);
439        assert_eq!(e5.links().unwrap().count(), 0);
440
441        assert!(e1.add_link(&mut e3).is_ok());
442
443        assert_eq!(e1.links().unwrap().count(), 2);
444        assert_eq!(e2.links().unwrap().count(), 1);
445        assert_eq!(e3.links().unwrap().count(), 1);
446        assert_eq!(e4.links().unwrap().count(), 0);
447        assert_eq!(e5.links().unwrap().count(), 0);
448
449        assert!(e1.add_link(&mut e4).is_ok());
450
451        assert_eq!(e1.links().unwrap().count(), 3);
452        assert_eq!(e2.links().unwrap().count(), 1);
453        assert_eq!(e3.links().unwrap().count(), 1);
454        assert_eq!(e4.links().unwrap().count(), 1);
455        assert_eq!(e5.links().unwrap().count(), 0);
456
457        assert!(e1.add_link(&mut e5).is_ok());
458
459        assert_eq!(e1.links().unwrap().count(), 4);
460        assert_eq!(e2.links().unwrap().count(), 1);
461        assert_eq!(e3.links().unwrap().count(), 1);
462        assert_eq!(e4.links().unwrap().count(), 1);
463        assert_eq!(e5.links().unwrap().count(), 1);
464
465        assert!(e5.remove_link(&mut e1).is_ok());
466
467        assert_eq!(e1.links().unwrap().count(), 3);
468        assert_eq!(e2.links().unwrap().count(), 1);
469        assert_eq!(e3.links().unwrap().count(), 1);
470        assert_eq!(e4.links().unwrap().count(), 1);
471        assert_eq!(e5.links().unwrap().count(), 0);
472
473        assert!(e4.remove_link(&mut e1).is_ok());
474
475        assert_eq!(e1.links().unwrap().count(), 2);
476        assert_eq!(e2.links().unwrap().count(), 1);
477        assert_eq!(e3.links().unwrap().count(), 1);
478        assert_eq!(e4.links().unwrap().count(), 0);
479        assert_eq!(e5.links().unwrap().count(), 0);
480
481        assert!(e3.remove_link(&mut e1).is_ok());
482
483        assert_eq!(e1.links().unwrap().count(), 1);
484        assert_eq!(e2.links().unwrap().count(), 1);
485        assert_eq!(e3.links().unwrap().count(), 0);
486        assert_eq!(e4.links().unwrap().count(), 0);
487        assert_eq!(e5.links().unwrap().count(), 0);
488
489        assert!(e2.remove_link(&mut e1).is_ok());
490
491        assert_eq!(e1.links().unwrap().count(), 0);
492        assert_eq!(e2.links().unwrap().count(), 0);
493        assert_eq!(e3.links().unwrap().count(), 0);
494        assert_eq!(e4.links().unwrap().count(), 0);
495        assert_eq!(e5.links().unwrap().count(), 0);
496
497    }
498
499    #[test]
500    fn test_link_deleting() {
501        setup_logging();
502        let store = get_store();
503
504        let mut e1 = store.retrieve(PathBuf::from("1")).unwrap();
505        let mut e2 = store.retrieve(PathBuf::from("2")).unwrap();
506
507        assert_eq!(e1.links().unwrap().count(), 0);
508        assert_eq!(e2.links().unwrap().count(), 0);
509
510        assert!(e1.add_link(&mut e2).is_ok());
511
512        assert_eq!(e1.links().unwrap().count(), 1);
513        assert_eq!(e2.links().unwrap().count(), 1);
514
515        assert!(e1.remove_link(&mut e2).is_ok());
516
517        assert_eq!(e1.links().unwrap().count(), 0);
518        assert_eq!(e2.links().unwrap().count(), 0);
519    }
520
521    #[test]
522    fn test_link_deleting_multiple_links() {
523        setup_logging();
524        let store = get_store();
525
526        let mut e1 = store.retrieve(PathBuf::from("1")).unwrap();
527        let mut e2 = store.retrieve(PathBuf::from("2")).unwrap();
528        let mut e3 = store.retrieve(PathBuf::from("3")).unwrap();
529
530        assert_eq!(e1.links().unwrap().count(), 0);
531        assert_eq!(e2.links().unwrap().count(), 0);
532        assert_eq!(e3.links().unwrap().count(), 0);
533
534        assert!(e1.add_link(&mut e2).is_ok()); // 1-2
535        assert!(e1.add_link(&mut e3).is_ok()); // 1-2, 1-3
536
537        assert_eq!(e1.links().unwrap().count(), 2);
538        assert_eq!(e2.links().unwrap().count(), 1);
539        assert_eq!(e3.links().unwrap().count(), 1);
540
541        assert!(e2.add_link(&mut e3).is_ok()); // 1-2, 1-3, 2-3
542
543        assert_eq!(e1.links().unwrap().count(), 2);
544        assert_eq!(e2.links().unwrap().count(), 2);
545        assert_eq!(e3.links().unwrap().count(), 2);
546
547        assert!(e1.remove_link(&mut e2).is_ok()); // 1-3, 2-3
548
549        assert_eq!(e1.links().unwrap().count(), 1);
550        assert_eq!(e2.links().unwrap().count(), 1);
551        assert_eq!(e3.links().unwrap().count(), 2);
552
553        assert!(e1.remove_link(&mut e3).is_ok()); // 2-3
554
555        assert_eq!(e1.links().unwrap().count(), 0);
556        assert_eq!(e2.links().unwrap().count(), 1);
557        assert_eq!(e3.links().unwrap().count(), 1);
558
559        assert!(e2.remove_link(&mut e3).is_ok());
560
561        assert_eq!(e1.links().unwrap().count(), 0);
562        assert_eq!(e2.links().unwrap().count(), 0);
563        assert_eq!(e3.links().unwrap().count(), 0);
564    }
565
566    #[test]
567    fn test_directional_link() {
568        use libimagstore::store::Entry;
569
570        setup_logging();
571        let store      = get_store();
572        let mut entry1 = store.create(PathBuf::from("test_directional_link-1")).unwrap();
573        let mut entry2 = store.create(PathBuf::from("test_directional_link-2")).unwrap();
574
575        assert!(entry1.unidirectional_links().unwrap().next().is_none());
576        assert!(entry2.unidirectional_links().unwrap().next().is_none());
577
578        assert!(entry1.directional_links_to().unwrap().next().is_none());
579        assert!(entry2.directional_links_to().unwrap().next().is_none());
580
581        assert!(entry1.directional_links_from().unwrap().next().is_none());
582        assert!(entry2.directional_links_from().unwrap().next().is_none());
583
584        assert!(entry1.add_link_to(&mut entry2).is_ok());
585
586        assert_eq!(entry1.unidirectional_links().unwrap().collect::<Vec<_>>(), vec![]);
587        assert_eq!(entry2.unidirectional_links().unwrap().collect::<Vec<_>>(), vec![]);
588
589        let get_directional_links_to = |e: &Entry| -> Result<Vec<String>, _> {
590            e.directional_links_to()
591                .unwrap()
592                .map(|l| l.to_str())
593                .collect::<Result<Vec<_>, _>>()
594        };
595
596        let get_directional_links_from = |e: &Entry| {
597            e.directional_links_from()
598                .unwrap()
599                .map(|l| l.to_str())
600                .collect::<Result<Vec<_>, _>>()
601        };
602
603        {
604            let entry1_dir_links = get_directional_links_to(&entry1).unwrap();
605            assert_eq!(entry1_dir_links, vec!["test_directional_link-2"]);
606        }
607        {
608            let entry2_dir_links = get_directional_links_to(&entry2).unwrap();
609            assert!(entry2_dir_links.is_empty());
610        }
611
612        {
613            let entry1_dir_links = get_directional_links_from(&entry1).unwrap();
614            assert!(entry1_dir_links.is_empty());
615        }
616        {
617            let entry2_dir_links = get_directional_links_from(&entry2).unwrap();
618            assert_eq!(entry2_dir_links, vec!["test_directional_link-1"]);
619        }
620
621    }
622
623}