use std::cmp::Reverse; use log::warn; use maildir::Maildir; use crate::arguments::{SortInfo, SortKey, SortOrder}; use crate::error::Result; use crate::rfc822::{MailHeader, TopMailHeader}; fn from_or_sender<'a>(mh: &'a MailHeader) -> &'a str { if mh.from.len() == 0 { warn!("mail without from"); panic!() } if mh.from.len() == 1 { &mh.from[0].address } else { &mh.sender.as_ref().unwrap().address } } fn mid_to_rec_time(mid: &str) -> f64 { let Some(dec) = mid.find('.') else { warn!("Invaild mail-id {}", mid); return 0.0; }; let Some(sep) = mid[dec + 1..].find('.') else { return 0.0; }; mid[..dec + 1 + sep].parse().unwrap() } fn sort_by_and_take( mut entries: Vec, sortby: &SortInfo, s: usize, e: usize, ) -> Vec { match sortby.key { SortKey::Date => { match sortby.order { SortOrder::Ascending => entries.sort_by(|a, b| { mid_to_rec_time(a.id()) .partial_cmp(&mid_to_rec_time(b.id())) .unwrap() }), SortOrder::Descending => entries.sort_by(|b, a| { mid_to_rec_time(a.id()) .partial_cmp(&mid_to_rec_time(b.id())) .unwrap() }), } entries .drain(s..e) .filter_map(|me| me.try_into().map_err(|e| warn!("{}", e)).ok()) .collect() } SortKey::Size => { match sortby.order { SortOrder::Ascending => { entries.sort_by_cached_key(|a| a.path().metadata().map_or(0, |m| m.len())) } SortOrder::Descending => entries .sort_by_cached_key(|a| Reverse(a.path().metadata().map_or(0, |m| m.len()))), } entries .drain(s..e) .filter_map(|me| me.try_into().map_err(|e| warn!("{}", e)).ok()) .collect() } SortKey::Subject => { let mut x: Vec = entries .drain(..) .filter_map(|me| me.try_into().map_err(|e| warn!("{}", e)).ok()) .collect(); match sortby.order { SortOrder::Ascending => x.sort_by(|a, b| a.head.subject.cmp(&b.head.subject)), SortOrder::Descending => x.sort_by(|b, a| a.head.subject.cmp(&b.head.subject)), } x.drain(s..e).collect() } SortKey::Sender => { let mut x: Vec = entries .drain(..) .filter_map(|me| me.try_into().map_err(|e| warn!("{}", e)).ok()) .collect(); match sortby.order { SortOrder::Ascending => { x.sort_by(|a, b| from_or_sender(&a.head).cmp(from_or_sender(&b.head))) } SortOrder::Descending => { x.sort_by(|b, a| from_or_sender(&a.head).cmp(from_or_sender(&b.head))) } } x.drain(s..e).collect() } } } pub fn list(md: &Maildir, i: usize, j: usize, sortby: &SortInfo) -> Result> { for r in md.list_new() { match r { Err(e) => warn!("{}", e), Ok(me) => { if let Err(e) = md.move_new_to_cur(me.id()) { warn!("{}", e); } } }; } let a: Vec<_> = md.list_cur().filter_map(std::result::Result::ok).collect(); let start = std::cmp::min(a.len(), i); let end = std::cmp::min(a.len(), j); Ok(sort_by_and_take(a, sortby, start, end)) }