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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
use std::{process::exit, sync::Arc};
use ariadne::{Color, Label, Report, ReportKind, Source};
use clap::Parser;
use color_eyre::Result;
use email::{
backend::feature::BackendFeatureSource, config::Config, email::search_query,
envelope::list::ListEnvelopesOptions, search_query::SearchEmailsQuery,
};
use pimalaya_tui::{
himalaya::{backend::BackendBuilder, config::EnvelopesTable},
terminal::{cli::printer::Printer, config::TomlConfig as _},
};
use tracing::info;
use crate::{
account::arg::name::AccountNameFlag, config::TomlConfig,
folder::arg::name::FolderNameOptionalFlag,
};
/// Search and sort envelopes as a list.
///
/// This command allows you to list envelopes included in the given
/// folder, matching the given query.
#[derive(Debug, Parser)]
pub struct EnvelopeListCommand {
#[command(flatten)]
pub folder: FolderNameOptionalFlag,
/// The page number.
///
/// The page number starts from 1 (which is the default). Giving a
/// page number to big will result in a out of bound error.
#[arg(long, short, value_name = "NUMBER", default_value = "1")]
pub page: usize,
/// The page size.
///
/// Determine the amount of envelopes a page should contain.
#[arg(long, short = 's', value_name = "NUMBER")]
pub page_size: Option<usize>,
#[command(flatten)]
pub account: AccountNameFlag,
/// The maximum width the table should not exceed.
///
/// This argument will force the table not to exceed the given
/// width in pixels. Columns may shrink with ellipsis in order to
/// fit the width.
#[arg(long = "max-width", short = 'w')]
#[arg(name = "table_max_width", value_name = "PIXELS")]
pub table_max_width: Option<u16>,
/// The list envelopes filter and sort query.
///
/// The query can be a filter query, a sort query or both
/// together.
///
/// A filter query is composed of operators and conditions. There
/// is 3 operators and 8 conditions:
///
/// • not <condition> → filter envelopes that do not match the
/// condition
///
/// • <condition> and <condition> → filter envelopes that match
/// both conditions
///
/// • <condition> or <condition> → filter envelopes that match
/// one of the conditions
///
/// ◦ date <yyyy-mm-dd> → filter envelopes that match the given
/// date
///
/// ◦ before <yyyy-mm-dd> → filter envelopes with date strictly
/// before the given one
///
/// ◦ after <yyyy-mm-dd> → filter envelopes with date stricly
/// after the given one
///
/// ◦ from <pattern> → filter envelopes with senders matching the
/// given pattern
///
/// ◦ to <pattern> → filter envelopes with recipients matching
/// the given pattern
///
/// ◦ subject <pattern> → filter envelopes with subject matching
/// the given pattern
///
/// ◦ body <pattern> → filter envelopes with text bodies matching
/// the given pattern
///
/// ◦ flag <flag> → filter envelopes matching the given flag
///
/// A sort query starts by "order by", and is composed of kinds
/// and orders. There is 4 kinds and 2 orders:
///
/// • date [order] → sort envelopes by date
///
/// • from [order] → sort envelopes by sender
///
/// • to [order] → sort envelopes by recipient
///
/// • subject [order] → sort envelopes by subject
///
/// ◦ <kind> asc → sort envelopes by the given kind in ascending
/// order
///
/// ◦ <kind> desc → sort envelopes by the given kind in
/// descending order
///
/// Examples:
///
/// subject foo and body bar → filter envelopes containing "foo"
/// in their subject and "bar" in their text bodies
///
/// order by date desc subject → sort envelopes by descending date
/// (most recent first), then by ascending subject
///
/// subject foo and body bar order by date desc subject →
/// combination of the 2 previous examples
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
pub query: Option<Vec<String>>,
}
impl Default for EnvelopeListCommand {
fn default() -> Self {
Self {
folder: Default::default(),
page: 1,
page_size: Default::default(),
account: Default::default(),
query: Default::default(),
table_max_width: Default::default(),
}
}
}
impl EnvelopeListCommand {
pub async fn execute(self, printer: &mut impl Printer, config: &TomlConfig) -> Result<()> {
info!("executing list envelopes command");
let (toml_account_config, account_config) = config
.clone()
.into_account_configs(self.account.name.as_deref(), |c: &Config, name| {
c.account(name).ok()
})?;
let toml_account_config = Arc::new(toml_account_config);
let folder = &self.folder.name;
let page = 1.max(self.page) - 1;
let page_size = self
.page_size
.unwrap_or_else(|| account_config.get_envelope_list_page_size());
let backend = BackendBuilder::new(
toml_account_config.clone(),
Arc::new(account_config),
|builder| {
builder
.without_features()
.with_list_envelopes(BackendFeatureSource::Context)
},
)
.without_sending_backend()
.build()
.await?;
let query = self
.query
.map(|query| query.join(" ").parse::<SearchEmailsQuery>());
let query = match query {
None => None,
Some(Ok(query)) => Some(query),
Some(Err(main_err)) => {
let source = "query";
let search_query::error::Error::ParseError(errs, query) = &main_err;
for err in errs {
Report::build(ReportKind::Error, source, err.span().start)
.with_message(main_err.to_string())
.with_label(
Label::new((source, err.span().into_range()))
.with_message(err.reason().to_string())
.with_color(Color::Red),
)
.finish()
.eprint((source, Source::from(&query)))
.unwrap();
}
exit(0)
}
};
let opts = ListEnvelopesOptions {
page,
page_size,
query,
};
let envelopes = backend.list_envelopes(folder, opts).await?;
let table = EnvelopesTable::from(envelopes)
.with_some_width(self.table_max_width)
.with_some_preset(toml_account_config.envelope_list_table_preset())
.with_some_unseen_char(toml_account_config.envelope_list_table_unseen_char())
.with_some_replied_char(toml_account_config.envelope_list_table_replied_char())
.with_some_flagged_char(toml_account_config.envelope_list_table_flagged_char())
.with_some_attachment_char(toml_account_config.envelope_list_table_attachment_char())
.with_some_id_color(toml_account_config.envelope_list_table_id_color())
.with_some_flags_color(toml_account_config.envelope_list_table_flags_color())
.with_some_subject_color(toml_account_config.envelope_list_table_subject_color())
.with_some_sender_color(toml_account_config.envelope_list_table_sender_color())
.with_some_date_color(toml_account_config.envelope_list_table_date_color());
printer.out(table)
}
}