summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-01-03 12:32:56 +0100
committerMatthias Beyer <mail@beyermatthias.de>2020-01-03 16:55:32 +0100
commit4548ac8f6365e327892e3517ac26ec79aeaa5f68 (patch)
treef3f79180d5fa3af0a382b445c1e77665d9ff6d60
parentf79593d9b95172aa9b2039d2a5f1384167b14863 (diff)
Add Mail::get_thread() for getting all mails in the same thread as this mail
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--lib/domain/libimagmail/Cargo.toml1
-rw-r--r--lib/domain/libimagmail/src/lib.rs1
-rw-r--r--lib/domain/libimagmail/src/mail.rs65
3 files changed, 67 insertions, 0 deletions
diff --git a/lib/domain/libimagmail/Cargo.toml b/lib/domain/libimagmail/Cargo.toml
index 1585dd12..e0b1a891 100644
--- a/lib/domain/libimagmail/Cargo.toml
+++ b/lib/domain/libimagmail/Cargo.toml
@@ -26,6 +26,7 @@ toml-query = "0.9.2"
mailparse = "0.8.0"
filters = "0.3.0"
failure = "0.1.5"
+resiter = "0.4.0"
serde = "1.0.94"
serde_derive = "1.0.94"
diff --git a/lib/domain/libimagmail/src/lib.rs b/lib/domain/libimagmail/src/lib.rs
index 1a58df0a..3f6ddd3d 100644
--- a/lib/domain/libimagmail/src/lib.rs
+++ b/lib/domain/libimagmail/src/lib.rs
@@ -43,6 +43,7 @@ extern crate toml;
extern crate toml_query;
extern crate filters;
#[macro_use] extern crate failure;
+extern crate resiter;
extern crate serde;
#[macro_use] extern crate serde_derive;
diff --git a/lib/domain/libimagmail/src/mail.rs b/lib/domain/libimagmail/src/mail.rs
index b33b4f91..bdcee6a2 100644
--- a/lib/domain/libimagmail/src/mail.rs
+++ b/lib/domain/libimagmail/src/mail.rs
@@ -21,6 +21,7 @@ use failure::Fallible as Result;
use failure::ResultExt;
use failure::Error;
use toml_query::read::TomlValueReadExt;
+use resiter::Filter;
use libimagstore::store::Entry;
use libimagentryutil::isa::Is;
@@ -50,6 +51,7 @@ pub trait Mail : RefFassade + Linkable {
fn neighbors(&self) -> Result<StoreIdIterator>;
fn get_neighbors<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>>;
+ fn get_thread<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>>;
}
@@ -184,6 +186,69 @@ impl Mail for Entry {
.into_mail_iterator()
})
}
+
+ /// Get the full thread starting from this Mail
+ ///
+ /// This function recursively traverses the linked mails, assumes them all to be in the same
+ /// thread and returns an iterator over all Mails it finds in this way.
+ ///
+ /// # Warning
+ ///
+ /// If a Mail is linked to this mail (even transitively!) but is _not_ in the same thread, it
+ /// is considered to be in the same thread.
+ ///
+ /// This function works recursively. Keep that in mind for large threads. Because it needs to
+ /// collect() internally, it might take a lot of memory for large threads.
+ ///
+ /// # Return value
+ ///
+ /// This function returns an Iterator over StoreIds in the same thread as this mail itself.
+ /// It does not yield any qualification about the distance between a mail in this thread and
+ /// this very mail.
+ ///
+ fn get_thread<'a>(&self, store: &'a Store) -> Result<MailIterator<'a>> {
+ trace!("Getting thread, starting point at: {}", self.get_location());
+ let mut thread = vec![self.get_location().clone()];
+
+ fn traverse<'a>(entry: &'a Entry, thread: &mut Vec<StoreId>, store: &Store) -> Result<()> {
+ // Helper function to get neighbors of a Mail, but filtered
+ fn get_filtered_neighbors<'a>(entry: &'a Entry, skiplist: &[StoreId]) -> Result<Vec<StoreId>> {
+ trace!("Getting filtered neighbors of {}", entry.get_location());
+ entry.neighbors()?.filter_ok(|id| !skiplist.contains(id)).collect()
+ }
+
+ // Get the neighbors, filtered by StoreIds which are already in the thread
+ // Then iterate over them
+ for n in get_filtered_neighbors(entry, thread)? {
+ trace!("Fetching {}", n);
+
+ // Get the FileLockEntry for the StoreId, or fail if it cannot be found
+ let next_entry = store.get(n.clone())?.ok_or_else(|| format_err!("Cannot find {}", n))?;
+
+ // if the FileLockEntry is a Mail
+ if next_entry.is_mail()? {
+ trace!("{} is a Mail", n);
+ thread.push(n); // it belongs to the thread
+
+ // And then traverse further starting from the current Mail
+ traverse(&next_entry, thread, store)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ trace!("Starting traversing...");
+ traverse(self, &mut thread, store)?;
+ trace!("Finished traversing.");
+ trace!("Found {} entries in thread", thread.len());
+
+ let iter = StoreIdIterator::new(Box::new(thread.into_iter().map(Ok)))
+ .into_get_iter(store)
+ .into_mail_iterator();
+
+ Ok(iter)
+ }
}
#[derive(Debug)]