summaryrefslogtreecommitdiff
path: root/src/cmd/list.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/list.rs')
-rw-r--r--src/cmd/list.rs116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/cmd/list.rs b/src/cmd/list.rs
new file mode 100644
index 0000000..0ec0389
--- /dev/null
+++ b/src/cmd/list.rs
@@ -0,0 +1,116 @@
+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<maildir::MailEntry>,
+ sortby: &SortInfo,
+ s: usize,
+ e: usize,
+) -> Vec<TopMailHeader> {
+ 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<TopMailHeader> = 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<TopMailHeader> = 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<Vec<TopMailHeader>> {
+ 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))
+}