diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs index afa6fb4b..bbe18721 100644 --- a/melib/src/mailbox/email/mod.rs +++ b/melib/src/mailbox/email/mod.rs @@ -129,7 +129,8 @@ impl StrBuilder { let offset = self.offset; let length = self.length; String::from_utf8(s[offset..offset + length].to_vec()).unwrap() - } + } + #[cfg(test)] fn display_bytes<'a>(&self, b: &'a [u8]) -> &'a [u8] { &b[self.offset..(self.offset+self.length)] } diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs index 22be77f0..58cc9267 100644 --- a/melib/src/mailbox/mod.rs +++ b/melib/src/mailbox/mod.rs @@ -34,7 +34,7 @@ use mailbox::backends::{folder_default, Folder, MailBackend}; pub mod accounts; pub use mailbox::accounts::Account; pub mod thread; -pub use mailbox::thread::{build_threads, Container}; +pub use mailbox::thread::{build_threads, Container, Threads, SortOrder, SortField}; use std::option::Option; @@ -43,8 +43,7 @@ use std::option::Option; pub struct Mailbox { pub folder: Folder, pub collection: Vec, - pub threaded_collection: Vec, - pub threads: Vec, + pub threads: Threads, } impl Clone for Mailbox { @@ -52,21 +51,21 @@ impl Clone for Mailbox { Mailbox { folder: self.folder.clone(), collection: self.collection.clone(), - threaded_collection: self.threaded_collection.clone(), threads: self.threads.clone(), } } } - -impl Mailbox { - pub fn new_dummy() -> Self { +impl Default for Mailbox { + fn default() -> Self { Mailbox { folder: folder_default(), - collection: Vec::with_capacity(0), - threaded_collection: Vec::with_capacity(0), - threads: Vec::with_capacity(0), + collection: Vec::default(), + threads: Threads::default(), } } +} + +impl Mailbox { pub fn new( folder: &Folder, sent_folder: &Option>, @@ -74,20 +73,18 @@ impl Mailbox { ) -> Result { let mut collection: Vec = collection?; collection.sort_by(|a, b| a.date().cmp(&b.date())); - let (threads, threaded_collection) = build_threads(&mut collection, sent_folder); + let threads = build_threads(&mut collection, sent_folder); Ok(Mailbox { folder: (*folder).clone(), collection: collection, threads: threads, - threaded_collection: threaded_collection, }) } pub fn len(&self) -> usize { self.collection.len() } pub fn threaded_mail(&self, i: usize) -> usize { - let thread = self.threads[self.threaded_collection[i]]; - thread.message().unwrap() + self.threads.thread_to_mail(i) } pub fn mail_and_thread(&mut self, i: usize) -> (&mut Envelope, Container) { let x = &mut self.collection.as_mut_slice()[i]; diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index fda6db66..e4fb6edd 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -30,6 +30,57 @@ use mailbox::Mailbox; extern crate fnv; use self::fnv::FnvHashMap; use std::borrow::Cow; +use std::ops::{Index, }; +use std::str::FromStr; +use std::result::Result as StdResult; +use std::cell::{Ref, RefCell}; +use std::cmp::Ordering; + +#[derive(Debug, Clone, PartialEq, Copy)] +pub enum SortOrder { + Asc, + Desc, +} + +#[derive(Debug, Clone, PartialEq, Copy)] +pub enum SortField { + Subject, + Date, +} + +impl Default for SortField { + fn default() -> Self { + SortField::Date + } +} + +impl Default for SortOrder { + fn default() -> Self { + SortOrder::Desc + } +} + +impl FromStr for SortField { + type Err = (); + fn from_str(s: &str) -> StdResult { + match s.trim() { + "subject" | "s" | "sub" | "sbj" | "subj" => Ok(SortField::Subject), + "date" | "d" => Ok(SortField::Date), + _ => Err(()), + } + } +} + +impl FromStr for SortOrder { + type Err = (); + fn from_str(s: &str) -> StdResult { + match s.trim() { + "asc" => Ok(SortOrder::Asc), + "desc" => Ok(SortOrder::Desc), + _ => Err(()), + } + } +} type UnixTimestamp = u64; @@ -52,6 +103,274 @@ pub struct Container { show_subject: bool, } +#[derive(Clone, Debug)] +struct ContainerTree { + id: usize, + children: Option>, +} + +impl ContainerTree { + fn new(id: usize) -> Self { + ContainerTree { + id, + children: None, + } + } +} + + +#[derive(Clone, Debug, Default)] +pub struct Threads { + containers: Vec, + threaded_collection: Vec, + root_set: Vec, + tree: RefCell>, + sort: RefCell<(SortField, SortOrder)>, + subsort: RefCell<(SortField, SortOrder)>, +} + +pub struct ThreadIterator<'a> { + pos: usize, + stack: Vec, + tree: Ref<'a ,Vec>, +} +impl<'a> Iterator for ThreadIterator<'a> { + type Item = usize; + fn next(&mut self) -> Option { + { + let mut tree = &(*self.tree); + for i in &self.stack { + tree = tree[*i].children.as_ref().unwrap(); + } + if self.pos == tree.len() { + if self.stack.is_empty() { + return None; + } + self.pos = self.stack.pop().unwrap() + 1; + } else { + debug_assert!(self.pos < tree.len()); + let ret = tree[self.pos].id; + if tree[self.pos].children.is_some() { + self.stack.push(self.pos); + self.pos = 0; + return Some(ret); + } + self.pos += 1; + return Some(ret); + } + } + return self.next(); + } +} + +impl<'a> IntoIterator for &'a Threads { + type Item = usize; + type IntoIter = ThreadIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + ThreadIterator { pos: 0, stack: Vec::new(), tree: self.tree.borrow() } + } +} + + + +impl Threads { + pub fn thread_to_mail(&self, i: usize) -> usize { + let thread = self.containers[self.threaded_collection[i]]; + thread.message().unwrap() + } + pub fn threaded_collection(&self) -> &Vec { + &self.threaded_collection + } + pub fn containers(&self) -> &Vec { + &self.containers + } + + fn inner_subsort_by(&self, subsort: (SortField, SortOrder), collection: &[Envelope]) { + let tree = &mut self.tree.borrow_mut(); + let containers = &self.containers; + for mut t in tree.iter_mut() { + if let Some(ref mut c ) = t.children { + c.sort_by(|a, b| { match subsort { + (SortField::Date, SortOrder::Desc) => { + let a = containers[a.id]; + let b = containers[b.id]; + b.date.cmp(&a.date) + } + (SortField::Date, SortOrder::Asc) => { + let a = containers[a.id]; + let b = containers[b.id]; + a.date.cmp(&b.date) + } + (SortField::Subject, SortOrder::Desc) => { + let a = containers[a.id].message(); + let b = containers[b.id].message(); + + if a.is_none() || b.is_none() { + return Ordering::Equal; + } + let ma = &collection[a.unwrap()]; + let mb = &collection[b.unwrap()]; + ma.subject().cmp(&mb.subject()) + } + (SortField::Subject, SortOrder::Asc) => { + let a = containers[a.id].message(); + let b = containers[b.id].message(); + + if a.is_none() || b.is_none() { + return Ordering::Equal; + } + let ma = &collection[a.unwrap()]; + let mb = &collection[b.unwrap()]; + mb.subject().cmp(&ma.subject()) + } + } + }); + } + } + } + + fn inner_sort_by(&self, sort: (SortField, SortOrder), collection: &[Envelope]) { + let tree = &mut self.tree.borrow_mut(); + let containers = &self.containers; + tree.sort_by(|a, b| { match sort { + (SortField::Date, SortOrder::Desc) => { + let a = containers[a.id]; + let b = containers[b.id]; + b.date.cmp(&a.date) + + } + (SortField::Date, SortOrder::Asc) => { + let a = containers[a.id]; + let b = containers[b.id]; + a.date.cmp(&b.date) + } + (SortField::Subject, SortOrder::Desc) => { + let a = containers[a.id].message(); + let b = containers[b.id].message(); + + if a.is_none() || b.is_none() { + return Ordering::Equal; + } + let ma = &collection[a.unwrap()]; + let mb = &collection[b.unwrap()]; + ma.subject().cmp(&mb.subject()) + } + (SortField::Subject, SortOrder::Asc) => { + let a = containers[a.id].message(); + let b = containers[b.id].message(); + + if a.is_none() || b.is_none() { + return Ordering::Equal; + } + let ma = &collection[a.unwrap()]; + let mb = &collection[b.unwrap()]; + mb.subject().cmp(&ma.subject()) + } + } + }); + } + pub fn sort_by(&self, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), collection: &[Envelope]) { + if *self.sort.borrow() != sort { + self.inner_sort_by(sort, collection); + *self.sort.borrow_mut() = sort; + } + if *self.subsort.borrow() != subsort { + self.inner_subsort_by(subsort, collection); + *self.subsort.borrow_mut() = subsort; + } + + } + + pub fn build_collection(&mut self, collection: &[Envelope]) { + fn build_threaded( + tree: &mut ContainerTree, + containers: &mut Vec, + indentation: usize, + threaded: &mut Vec, + i: usize, + root_subject_idx: usize, + collection: &[Envelope], + ) { + tree.id = i; + let thread = containers[i]; + if let Some(msg_idx) = containers[root_subject_idx].message() { + let root_subject = collection[msg_idx].subject(); + /* If the Container has no Message, but does have children, remove this container but + * promote its children to this level (that is, splice them in to the current child + * list.) */ + if indentation > 0 && thread.has_message() { + let subject = collection[thread.message().unwrap()].subject(); + if subject == root_subject + || subject.starts_with("Re: ") + && subject.as_ref().ends_with(root_subject.as_ref()) + { + containers[i].set_show_subject(false); + } + } + } + if thread.has_parent() && !containers[thread.parent().unwrap()].has_message() { + containers[i].parent = None; + } + let indentation = if thread.has_message() { + containers[i].set_indentation(indentation); + if !threaded.contains(&i) { + threaded.push(i); + } + indentation + 1 + } else if indentation > 0 { + indentation + } else { + indentation + 1 + }; + + if thread.has_children() { + let mut child_vec = Vec::new(); + + let mut fc = thread.first_child().unwrap(); + + loop { + let mut new_child_tree = ContainerTree::new(fc); + build_threaded(&mut new_child_tree, containers, indentation, threaded, fc, i, collection); + child_vec.push(new_child_tree); + let thread_ = containers[fc]; + if !thread_.has_sibling() { + break; + } + fc = thread_.next_sibling().unwrap(); + } + tree.children = Some(child_vec); + } + } + let mut tree = Vec::new(); + for i in &self.root_set { + let mut tree_node = ContainerTree::new(*i); + build_threaded( + &mut tree_node, + &mut self.containers, + 0, + &mut self.threaded_collection, + *i, + *i, + collection, + ); + tree.push(tree_node); + } + self.tree.replace(tree); + self.inner_sort_by(*self.sort.borrow(), collection); + self.inner_subsort_by(*self.subsort.borrow(), collection); + } +} + +impl Index for Threads { + type Output = Container; + + fn index(&self, index: usize) -> &Container { + self.containers.get(index).expect("thread index out of bounds") + } +} + + impl Container { pub fn date(&self) -> UnixTimestamp { self.date @@ -124,7 +443,7 @@ fn build_collection( threads: &mut Vec, id_table: &mut FnvHashMap, usize>, collection: &mut [Envelope], -) -> () { + ) -> () { for (i, x) in collection.iter_mut().enumerate() { let x_index; /* x's index in threads */ let m_id = x.message_id_raw().into_owned(); @@ -179,7 +498,7 @@ fn build_collection( let parent_id = if id_table.contains_key(&r) { let p = id_table[r.as_ref()]; if !(threads[p].is_descendant(threads, &threads[curr_ref]) - || threads[curr_ref].is_descendant(threads, &threads[p])) + || threads[curr_ref].is_descendant(threads, &threads[p])) { threads[curr_ref].parent = Some(p); if threads[p].first_child.is_none() { @@ -239,7 +558,7 @@ fn build_collection( pub fn build_threads( collection: &mut Vec, sent_folder: &Option>, -) -> (Vec, Vec) { + ) -> Threads { /* To reconstruct thread information from the mails we need: */ /* a vector to hold thread members */ @@ -273,77 +592,77 @@ pub fn build_threads( if id_table.contains_key(&m_id) || (!x.in_reply_to_raw().is_empty() && id_table.contains_key(&x.in_reply_to_raw())) - { - let mut x: Envelope = (*x).clone(); - if id_table.contains_key(&m_id) { - let c = id_table[&m_id]; - if threads[c].message.is_some() { - /* skip duplicate message-id, but this should be handled instead */ - continue; - } - threads[c].message = Some(idx); - assert!(threads[c].has_children()); - threads[c].date = x.date(); - x.set_thread(c); - } else if !x.in_reply_to_raw().is_empty() - && id_table.contains_key(&x.in_reply_to_raw()) { - let p = id_table[&x_r_id]; - let c = if id_table.contains_key(&m_id) { - id_table[&m_id] - } else { - threads.push(Container { - message: Some(idx), - id: tidx, - parent: Some(p), - first_child: None, - next_sibling: None, - date: x.date(), - indentation: 0, - show_subject: true, - }); - id_table.insert(Cow::from(m_id.into_owned()), tidx); - x.set_thread(tidx); - tidx += 1; - tidx - 1 - }; - threads[c].parent = Some(p); - if threads[p].is_descendant(&threads, &threads[c]) - || threads[c].is_descendant(&threads, &threads[p]) - { - continue; - } - if threads[p].first_child.is_none() { - threads[p].first_child = Some(c); - } else { - let mut fc = threads[p].first_child.unwrap(); - while threads[fc].next_sibling.is_some() { - threads[fc].parent = Some(p); - fc = threads[fc].next_sibling.unwrap(); - } - threads[fc].next_sibling = Some(c); - threads[fc].parent = Some(p); - } - /* update thread date */ - let mut parent_iter = p; - 'date: loop { - let p = &mut threads[parent_iter]; - if p.date < x.date() { - p.date = x.date(); + let mut x: Envelope = (*x).clone(); + if id_table.contains_key(&m_id) { + let c = id_table[&m_id]; + if threads[c].message.is_some() { + /* skip duplicate message-id, but this should be handled instead */ + continue; } - match p.parent { - Some(p) => { - parent_iter = p; + threads[c].message = Some(idx); + assert!(threads[c].has_children()); + threads[c].date = x.date(); + x.set_thread(c); + } else if !x.in_reply_to_raw().is_empty() + && id_table.contains_key(&x.in_reply_to_raw()) + { + let p = id_table[&x_r_id]; + let c = if id_table.contains_key(&m_id) { + id_table[&m_id] + } else { + threads.push(Container { + message: Some(idx), + id: tidx, + parent: Some(p), + first_child: None, + next_sibling: None, + date: x.date(), + indentation: 0, + show_subject: true, + }); + id_table.insert(Cow::from(m_id.into_owned()), tidx); + x.set_thread(tidx); + tidx += 1; + tidx - 1 + }; + threads[c].parent = Some(p); + if threads[p].is_descendant(&threads, &threads[c]) + || threads[c].is_descendant(&threads, &threads[p]) + { + continue; + } + if threads[p].first_child.is_none() { + threads[p].first_child = Some(c); + } else { + let mut fc = threads[p].first_child.unwrap(); + while threads[fc].next_sibling.is_some() { + threads[fc].parent = Some(p); + fc = threads[fc].next_sibling.unwrap(); + } + threads[fc].next_sibling = Some(c); + threads[fc].parent = Some(p); } - None => { - break 'date; + /* update thread date */ + let mut parent_iter = p; + 'date: loop { + let p = &mut threads[parent_iter]; + if p.date < x.date() { + p.date = x.date(); + } + match p.parent { + Some(p) => { + parent_iter = p; + } + None => { + break 'date; + } + } } } - } + collection.push(x); + idx += 1; } - collection.push(x); - idx += 1; - } } } } @@ -354,80 +673,28 @@ pub fn build_threads( if threads[*v].parent.is_none() { if !threads[*v].has_message() && threads[*v].has_children() - && !threads[threads[*v].first_child.unwrap()].has_sibling() - { - /* Do not promote the children if doing so would promote them to the root set - * -- unless there is only one child, in which case, do. */ - root_set.push(threads[*v].first_child.unwrap()); - continue 'root_set; - } + && !threads[threads[*v].first_child.unwrap()].has_sibling() + { + /* Do not promote the children if doing so would promote them to the root set + * -- unless there is only one child, in which case, do. */ + root_set.push(threads[*v].first_child.unwrap()); + + continue 'root_set; + } root_set.push(*v); } } root_set.sort_by(|a, b| threads[*b].date.cmp(&threads[*a].date)); /* Group messages together by thread in a collection so we can print them together */ - let mut threaded_collection: Vec = Vec::with_capacity(collection.len()); - fn build_threaded( - threads: &mut Vec, - indentation: usize, - threaded: &mut Vec, - i: usize, - root_subject_idx: usize, - collection: &[Envelope], - ) { - let thread = threads[i]; - if let Some(msg_idx) = threads[root_subject_idx].message() { - let root_subject = collection[msg_idx].subject(); - /* If the Container has no Message, but does have children, remove this container but - * promote its children to this level (that is, splice them in to the current child - * list.) */ - if indentation > 0 && thread.has_message() { - let subject = collection[thread.message().unwrap()].subject(); - if subject == root_subject - || subject.starts_with("Re: ") - && subject.as_ref().ends_with(root_subject.as_ref()) - { - threads[i].set_show_subject(false); - } - } - } - if thread.has_parent() && !threads[thread.parent().unwrap()].has_message() { - threads[i].parent = None; - } - let indentation = if thread.has_message() { - threads[i].set_indentation(indentation); - if !threaded.contains(&i) { - threaded.push(i); - } - indentation + 1 - } else if indentation > 0 { - indentation - } else { - indentation + 1 - }; - if thread.has_children() { - let mut fc = thread.first_child().unwrap(); - loop { - build_threaded(threads, indentation, threaded, fc, i, collection); - let thread_ = threads[fc]; - if !thread_.has_sibling() { - break; - } - fc = thread_.next_sibling().unwrap(); - } - } - } - for i in &root_set { - build_threaded( - &mut threads, - 0, - &mut threaded_collection, - *i, - *i, - collection, - ); - } + let threaded_collection: Vec = Vec::with_capacity(collection.len()); - (threads, threaded_collection) + let mut t = Threads { + containers: threads, + threaded_collection, + root_set, + ..Default::default() + }; + t.build_collection(&collection); + t } diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 46e84d86..9c500706 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -94,7 +94,7 @@ impl CompactMailListing { .as_ref() .unwrap(); - self.length = mailbox.threads.len(); + self.length = mailbox.threads.containers().len(); let mut content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' ')); if self.length == 0 { write_string_to_grid( @@ -113,8 +113,8 @@ impl CompactMailListing { let mut indentations: Vec = Vec::with_capacity(6); let mut thread_idx = 0; // needed for alternate thread colors /* Draw threaded view. */ - let mut local_collection: Vec = mailbox.threaded_collection.clone(); - let threads: &Vec = &mailbox.threads; + let mut local_collection: Vec = mailbox.threads.threaded_collection().clone(); + let threads: &Vec = &mailbox.threads.containers(); local_collection.sort_by(|a, b| match self.sort { (SortField::Date, SortOrder::Desc) => { mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date()) @@ -139,7 +139,8 @@ impl CompactMailListing { }); let mut iter = local_collection.iter().enumerate().peekable(); let len = mailbox - .threaded_collection + .threads + .threaded_collection() .len() .to_string() .chars() diff --git a/ui/src/components/mail/listing/mod.rs b/ui/src/components/mail/listing/mod.rs index 611de7ab..259a4c7b 100644 --- a/ui/src/components/mail/listing/mod.rs +++ b/ui/src/components/mail/listing/mod.rs @@ -34,8 +34,9 @@ pub struct MailListing { cursor_pos: (usize, usize, usize), new_cursor_pos: (usize, usize, usize), length: usize, + local_collection: Vec, sort: (SortField, SortOrder), - //subsort: (SortField, SortOrder), + subsort: (SortField, SortOrder), /// Cache current view. content: CellBuffer, /// If we must redraw on next redraw event @@ -75,8 +76,9 @@ impl MailListing { cursor_pos: (0, 1, 0), new_cursor_pos: (0, 0, 0), length: 0, - sort: (SortField::Date, SortOrder::Desc), - //subsort: (SortField::Date, SortOrder::Asc), + local_collection: Vec::new(), + sort: (Default::default(), Default::default()), + subsort: (Default::default(), Default::default()), content: content, dirty: true, unfocused: false, @@ -113,7 +115,7 @@ impl MailListing { .unwrap(); self.length = if threaded { - mailbox.threaded_collection.len() + mailbox.threads.threaded_collection().len() } else { mailbox.len() }; @@ -136,57 +138,33 @@ impl MailListing { let mut indentations: Vec = Vec::with_capacity(6); let mut thread_idx = 0; // needed for alternate thread colors /* Draw threaded view. */ - let mut local_collection: Vec = mailbox.threaded_collection.clone(); - let threads: &Vec = &mailbox.threads; - local_collection.sort_by(|a, b| match self.sort { - (SortField::Date, SortOrder::Desc) => { - let a = mailbox.thread(*a); - let b = mailbox.thread(*b); - let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; - let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; - mb.date().cmp(&ma.date()) - } - (SortField::Date, SortOrder::Asc) => { - let a = mailbox.thread(*a); - let b = mailbox.thread(*b); - let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; - let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; - ma.date().cmp(&mb.date()) - } - (SortField::Subject, SortOrder::Desc) => { - let a = mailbox.thread(*a); - let b = mailbox.thread(*b); - let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; - let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; - ma.subject().cmp(&mb.subject()) - } - (SortField::Subject, SortOrder::Asc) => { - let a = mailbox.thread(*a); - let b = mailbox.thread(*b); - let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; - let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; - mb.subject().cmp(&ma.subject()) - } - }); - let mut iter = local_collection.iter().enumerate().peekable(); - let len = mailbox - .threaded_collection + let threads = &mailbox.threads; + threads.sort_by(self.sort, self.subsort, &mailbox.collection); + let containers: &Vec = &threads.containers(); + let mut iter = threads.into_iter().peekable(); + let len = threads + .threaded_collection() .len() .to_string() .chars() .count(); /* This is just a desugared for loop so that we can use .peek() */ - while let Some((idx, i)) = iter.next() { - let container = &threads[*i]; + let mut idx = 0; + while let Some(i) = iter.next() { + let container = &containers[i]; let indentation = container.indentation(); if indentation == 0 { thread_idx += 1; } - assert!(container.has_message()); + if !container.has_message() { + continue; + } + + match iter.peek() { - Some(&(_, x)) if threads[*x].indentation() == indentation => { + Some(&x) if threads[x].indentation() == indentation => { indentations.pop(); indentations.push(true); } @@ -234,16 +212,17 @@ impl MailListing { } match iter.peek() { - Some(&(_, x)) if threads[*x].indentation() > indentation => { + Some(&x) if containers[x].indentation() > indentation => { indentations.push(false); } - Some(&(_, x)) if threads[*x].indentation() < indentation => { - for _ in 0..(indentation - threads[*x].indentation()) { + Some(&x) if containers[x].indentation() < indentation => { + for _ in 0..(indentation - containers[x].indentation()) { indentations.pop(); } } _ => {} } + idx += 1; } } else { // Populate `CellBuffer` with every entry. @@ -255,6 +234,40 @@ impl MailListing { break; } /* Write an entire line for each envelope entry. */ + /* + self.local_collection = (0..mailbox.collection.len()).collect(); + let sort = self.sort; + self.local_collection.sort_by(|a, b| match sort { + (SortField::Date, SortOrder::Desc) => { + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + mb.date().cmp(&ma.date()) + } + (SortField::Date, SortOrder::Asc) => { + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + ma.date().cmp(&mb.date()) + } + (SortField::Subject, SortOrder::Desc) => { + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + ma.subject().cmp(&mb.subject()) + } + (SortField::Subject, SortOrder::Asc) => { + let a = mailbox.thread(*a); + let b = mailbox.thread(*b); + let ma = &mailbox.collection[*a.message().as_ref().unwrap()]; + let mb = &mailbox.collection[*b.message().as_ref().unwrap()]; + mb.subject().cmp(&ma.subject()) + } + }); + */ let envelope: &Envelope = &mailbox.collection[idx]; let fg_color = if !envelope.is_seen() { @@ -752,13 +765,21 @@ impl Component for MailListing { self.refresh_mailbox(context); return; } + Action::SubSort(field, order) => { + eprintln!("SubSort {:?} , {:?}", field, order); + self.subsort = (*field, *order); + self.dirty = true; + self.refresh_mailbox(context); + return; + } Action::Sort(field, order) => { - self.sort = (field.clone(), order.clone()); + eprintln!("Sort {:?} , {:?}", field, order); + self.sort = (*field, *order); self.dirty = true; self.refresh_mailbox(context); return; } - _ => {} + // _ => {} }, _ => {} } diff --git a/ui/src/execute/actions.rs b/ui/src/execute/actions.rs index 021473ae..3fd12033 100644 --- a/ui/src/execute/actions.rs +++ b/ui/src/execute/actions.rs @@ -23,46 +23,13 @@ * User actions that need to be handled by the UI */ -use std::str::FromStr; +pub use melib::mailbox::{SortOrder, SortField}; #[derive(Debug, Clone)] pub enum MailListingAction { ToggleThreaded, } -#[derive(Debug, Clone)] -pub enum SortOrder { - Asc, - Desc, -} - -#[derive(Debug, Clone)] -pub enum SortField { - Subject, - Date, -} - -impl FromStr for SortField { - type Err = (); - fn from_str(s: &str) -> Result { - match s.trim() { - "subject" | "s" | "sub" | "sbj" | "subj" => Ok(SortField::Subject), - "date" | "d" => Ok(SortField::Date), - _ => Err(()), - } - } -} - -impl FromStr for SortOrder { - type Err = (); - fn from_str(s: &str) -> Result { - match s.trim() { - "asc" => Ok(SortOrder::Asc), - "desc" => Ok(SortOrder::Desc), - _ => Err(()), - } - } -} #[derive(Debug, Clone)] pub enum Action {