1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
use std::cmp::Reverse;
use std::path::PathBuf;
use log::warn;
use protobuf::Message as _;
use crate::cmd::open_submaildir;
use crate::error::Result;
use crate::pb3::jwebmail::{ListMailHeader, ListReq, ListResp, MailHeader};
use crate::rfc822::me_to_lmh;
fn from_or_sender(mh: &MailHeader) -> &str {
if mh.written_from.is_empty() {
warn!("mail without from");
panic!()
}
if mh.written_from.len() == 1 {
&mh.written_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: &str,
s: usize,
e: usize,
) -> Vec<ListMailHeader> {
match sortby {
"date" => {
entries.sort_by(|a, b| {
mid_to_rec_time(a.id())
.partial_cmp(&mid_to_rec_time(b.id()))
.unwrap()
});
entries
.drain(s..e)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect()
}
"!date" => {
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_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect()
}
"size" => {
entries.sort_by_cached_key(|a| a.path().metadata().map_or(0, |m| m.len()));
entries
.drain(s..e)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect()
}
"!size" => {
entries.sort_by_cached_key(|a| Reverse(a.path().metadata().map_or(0, |m| m.len())));
entries
.drain(s..e)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect()
}
"subject" | "!subject" => {
let mut x: Vec<ListMailHeader> = entries
.drain(..)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect();
if sortby.as_bytes().first().copied() == Some(b'!') {
x.sort_by(|a, b| a.header.subject.cmp(&b.header.subject))
} else {
x.sort_by(|b, a| a.header.subject.cmp(&b.header.subject))
}
x.drain(s..e).collect()
}
"sender" | "!sender" => {
let mut x: Vec<ListMailHeader> = entries
.drain(..)
.filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
.collect();
if sortby.as_bytes().first().copied() != Some(b'!') {
x.sort_by(|a, b| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
} else {
x.sort_by(|b, a| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
}
x.drain(s..e).collect()
}
_ => todo!(),
}
}
pub fn list(path: PathBuf, req: &[u8]) -> Result<Vec<u8>> {
let r = ListReq::parse_from_bytes(req)?;
let md = open_submaildir(path, &r.folder);
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(), r.start as usize);
let end = std::cmp::min(a.len(), r.end as usize);
let mut resp = ListResp::new();
resp.mail_heads = sort_by_and_take(a, &r.sort, start, end);
resp.write_to_bytes().map_err(|e| e.into())
}
|