summaryrefslogtreecommitdiff
path: root/src/cmd/list.rs
blob: 8aeaf97a5752714a3e72f1cd81589cf932090475 (plain)
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::cmp::Reverse;

use log::warn;

use crate::cmd::{open_submaildir, MailStorage};
use crate::de_jmhoffmann_jwebmail_mailstorage::{
    Call_List, ListMailHeader, MailHeader, Sort, Sort_direction, Sort_parameter,
};
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()
        .or_else(|_| mid[..dec].parse())
        .unwrap_or(0.0)
}

fn sort_by_and_take(
    mut entries: Vec<maildir::MailEntry>,
    sortby: Sort,
    s: usize,
    e: usize,
) -> Vec<ListMailHeader> {
    match sortby.parameter {
        Sort_parameter::date => match sortby.direction {
            Sort_direction::asc => {
                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()
            }
            Sort_direction::desc => {
                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()
            }
        },
        Sort_parameter::size => match sortby.direction {
            Sort_direction::asc => {
                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()
            }
            Sort_direction::desc => {
                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()
            }
        },
        Sort_parameter::subject => {
            let mut x: Vec<ListMailHeader> = entries
                .drain(..)
                .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
                .collect();
            match sortby.direction {
                Sort_direction::asc => x.sort_by(|a, b| a.header.subject.cmp(&b.header.subject)),
                Sort_direction::desc => x.sort_by(|b, a| a.header.subject.cmp(&b.header.subject)),
            }
            x.drain(s..e).collect()
        }
        Sort_parameter::sender => {
            let mut x: Vec<ListMailHeader> = entries
                .drain(..)
                .filter_map(|me| me_to_lmh(me).map_err(|e| warn!("{}", e)).ok())
                .collect();
            match sortby.direction {
                Sort_direction::asc => {
                    x.sort_by(|a, b| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
                }
                Sort_direction::desc => {
                    x.sort_by(|b, a| from_or_sender(&a.header).cmp(from_or_sender(&b.header)))
                }
            }
            x.drain(s..e).collect()
        }
    }
}

pub fn list(
    ms: &MailStorage,
    call: &mut dyn Call_List,
    folder: String,
    start: i64,
    end: i64,
    sort: Sort,
) -> varlink::Result<()> {
    if let Some(path) = ms.maildir_path.read().unwrap().clone() {
        let md = open_submaildir(path, &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(), start as usize);
        let end = std::cmp::min(a.len(), end as usize);

        let mail_heads = sort_by_and_take(a, sort, start, end);

        call.reply(mail_heads)
    } else {
        call.reply_not_initialized()
    }
}