diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs index d9d9eb84..9e0a8a6a 100644 --- a/melib/src/mailbox/mod.rs +++ b/melib/src/mailbox/mod.rs @@ -46,7 +46,7 @@ pub struct Mailbox { pub folder: Folder, pub collection: Vec, pub threaded_collection: Vec, - threads: Vec, + pub threads: Vec, } impl Mailbox { diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs index 8207de19..8684ab93 100644 --- a/melib/src/mailbox/thread.rs +++ b/melib/src/mailbox/thread.rs @@ -53,6 +53,9 @@ pub struct Container { } impl Container { + pub fn date(&self) -> UnixTimestamp { + self.date + } pub fn message(&self) -> Option { self.message } diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 80f47e4c..01cd46cf 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -10,7 +10,8 @@ pub struct MailListing { cursor_pos: (usize, usize, usize), new_cursor_pos: (usize, usize, usize), length: usize, - // TODO: sorting + sort: (SortField, SortOrder), + subsort: (SortField, SortOrder), /// Cache current view. content: CellBuffer, /// If we must redraw on next redraw event @@ -38,6 +39,8 @@ 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), content: content, dirty: true, unfocused: false, @@ -99,7 +102,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 iter = mailbox.threaded_collection.iter().enumerate().peekable(); + let mut local_collection: Vec = mailbox.threaded_collection.clone(); + let mut threads: Vec<&Container> = mailbox.threads.iter().map(|v| v).collect(); + local_collection.sort_by(|a, b| { + match self.sort { + (SortField::Date, SortOrder::Desc) => { + mailbox.thread(*b).date().cmp(&mailbox.thread(*a).date()) + }, + (SortField::Date, SortOrder::Asc) => { + mailbox.thread(*a).date().cmp(&mailbox.thread(*b).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 .len() @@ -108,16 +137,16 @@ impl MailListing { .count(); /* This is just a desugared for loop so that we can use .peek() */ while let Some((idx, i)) = iter.next() { - let container = mailbox.thread(*i); + let container = threads[*i]; let indentation = container.indentation(); if indentation == 0 { thread_idx += 1; } - assert_eq!(container.has_message(), true); + assert!(container.has_message() == true); match iter.peek() { - Some(&(_, x)) if mailbox.thread(*x).indentation() == indentation => { + Some(&(_, x)) if threads[*x].indentation() == indentation => { indentations.pop(); indentations.push(true); } @@ -164,11 +193,11 @@ impl MailListing { } match iter.peek() { - Some(&(_, x)) if mailbox.thread(*x).indentation() > indentation => { + Some(&(_, x)) if threads[*x].indentation() > indentation => { indentations.push(false); } - Some(&(_, x)) if mailbox.thread(*x).indentation() < indentation => { - for _ in 0..(indentation - mailbox.thread(*x).indentation()) { + Some(&(_, x)) if threads[*x].indentation() < indentation => { + for _ in 0..(indentation - threads[*x].indentation()) { indentations.pop(); } } @@ -661,7 +690,13 @@ impl Component for MailListing { self.refresh_mailbox(context); return; }, - //_ => {}, + Action::Sort(field, order) => { + self.sort = (field.clone(), order.clone()); + self.dirty = true; + self.refresh_mailbox(context); + return; + }, + _ => {}, }, _ => {} } diff --git a/ui/src/execute/actions.rs b/ui/src/execute/actions.rs index bf556c4d..e615670b 100644 --- a/ui/src/execute/actions.rs +++ b/ui/src/execute/actions.rs @@ -2,13 +2,65 @@ * User actions that need to be handled by the UI */ -#[derive(Debug)] +use std::str::FromStr; + +#[derive(Debug, Clone)] pub enum MailListingAction { ToggleThreaded, } -#[derive(Debug)] +#[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 { + eprintln!("sortfield from_str {}", s); + match s.trim() { + "subject" | "s" | "sub" | "sbj" | "subj" => { + eprintln!("parsed: subject"); + } + "date" | "d" => { + eprintln!("parsed date"); + } + _ => { + eprintln!("error in parse"); + } + } + 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 { + eprintln!("sortoder from_str {}", s); + match s.trim() { + "asc" => Ok(SortOrder::Asc), + "desc" => Ok(SortOrder::Desc), + _ => Err(()) + } + } +} + +#[derive(Debug, Clone)] pub enum Action { MailListing(MailListingAction), ViewMailbox(usize), + Sort(SortField, SortOrder), + SubSort(SortField, SortOrder), } diff --git a/ui/src/execute/mod.rs b/ui/src/execute/mod.rs index e8933501..d37d2351 100644 --- a/ui/src/execute/mod.rs +++ b/ui/src/execute/mod.rs @@ -1,6 +1,6 @@ /*! A parser module for user commands passed through the Ex mode. */ -use nom::{digit, }; +use nom::{digit, not_line_ending}; use std; pub mod actions; pub use actions::*; @@ -14,15 +14,40 @@ named!( ) ); +named!(sortfield, + map_res!( + map_res!(take_until_s!(" "), std::str::from_utf8), + std::str::FromStr::from_str)); + + +named!(sortorder, + map_res!( + map_res!(call!(not_line_ending), std::str::from_utf8), + std::str::FromStr::from_str)); + + + named!(goto, preceded!(tag!("b "), map!(call!(usize_c), |v| Action::ViewMailbox(v)) )); -//named!(sort<&str>, -// preceded!(tag!("sort "), -// map_res!(call!(alpha), std::str::from_utf8)) -// ); +named!(subsort, do_parse!( + tag!("subsort ") >> + p: pair!(sortfield,sortorder) >> + ( + Action::SubSort(p.0, p.1) + + ) + )); +named!(sort, do_parse!( + tag!("sort ") >> + p: separated_pair!(sortfield,tag!(" "), sortorder) >> + ( + Action::Sort(p.0, p.1) + + ) + )); named!(threaded, map!(ws!(tag!("threaded")), |_| Action::MailListing(MailListingAction::ToggleThreaded))); @@ -30,6 +55,6 @@ named!(toggle, preceded!(tag!("toggle "), alt_complete!( threaded ))); -named!(pub parse_command, - alt_complete!( goto | toggle) +named!(pub parse_command, + alt_complete!( goto | toggle | sort | subsort) ); diff --git a/ui/src/state.rs b/ui/src/state.rs index 2a335ede..f910a391 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -313,7 +313,9 @@ impl State { } /// Convert user commands to actions/method calls. fn parse_command(&mut self, cmd: String) { + eprintln!("cmd is {}", cmd); let result = parse_command(&cmd.as_bytes()).to_full_result(); + eprintln!("rseult is {:?}", result); if let Ok(v) = result { self.rcv_event(UIEvent { id: 0, event_type: UIEventType::Action(v) }); @@ -399,7 +401,6 @@ impl State { } } - pub fn try_wait_on_child(&mut self) -> Option { if { match self.child {