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
#![allow(clippy::wildcard_imports)]
use super::*;
impl ImapConnection {
// -----------------------------------------------------------------------
// SORT (RFC 5256 Section 2)
// -----------------------------------------------------------------------
/// SORT — server-side sorting by sequence number (RFC 5256 Section 2).
///
/// Returns matching sequence numbers sorted by the given sort keys,
/// along with an optional MODSEQ value (RFC 7162 Section 3.1.5).
///
/// `sort_criteria` is a space-separated list of sort keys (e.g.
/// `"REVERSE DATE SUBJECT"`). The encoder wraps them in parentheses
/// per the SORT ABNF: `sort-criteria = "(" sort-key *(SP sort-key) ")"`.
///
/// Requires the `SORT` capability (RFC 5256 Section 1).
pub async fn sort(
&self,
sort_criteria: &str,
charset: &str,
criteria: impl AsRef<str>,
timeout: Duration,
) -> Result<SearchResult, Error> {
let criteria = criteria.as_ref();
self.check_utf8_only_enforced()?;
self.require_state(&[SessionState::Selected])?;
self.validate_search_criteria_capabilities(criteria)?;
{
let snap = self.state_rx.borrow();
if !snap.capabilities.contains(&Capability::Sort) {
return Err(Error::MissingCapability("SORT".into()));
}
}
let cmd = Command::Sort {
sort_criteria: sort_criteria.to_owned(),
charset: charset.to_owned(),
criteria: criteria.to_owned(),
};
tokio::time::timeout(
timeout,
self.submit_regular(cmd, super::dispatch::SortConsumer::default()),
)
.await
.map_err(|_| Error::Timeout)?
}
/// UID SORT — server-side sorting returning UIDs (RFC 5256 Section 2).
///
/// Like [`sort`](Self::sort) but returns UIDs instead of sequence
/// numbers.
///
/// Requires the `SORT` capability (RFC 5256 Section 1).
pub async fn uid_sort(
&self,
sort_criteria: &str,
charset: &str,
criteria: impl AsRef<str>,
timeout: Duration,
) -> Result<SearchResult, Error> {
let criteria = criteria.as_ref();
self.check_utf8_only_enforced()?;
self.require_state(&[SessionState::Selected])?;
self.validate_search_criteria_capabilities(criteria)?;
{
let snap = self.state_rx.borrow();
if !snap.capabilities.contains(&Capability::Sort) {
return Err(Error::MissingCapability("SORT".into()));
}
}
let cmd = Command::UidSort {
sort_criteria: sort_criteria.to_owned(),
charset: charset.to_owned(),
criteria: criteria.to_owned(),
};
tokio::time::timeout(
timeout,
self.submit_regular(cmd, super::dispatch::SortConsumer::default()),
)
.await
.map_err(|_| Error::Timeout)?
}
// -----------------------------------------------------------------------
// THREAD (RFC 5256 Section 3)
// -----------------------------------------------------------------------
/// THREAD — server-side threading by sequence number
/// (RFC 5256 Section 3).
///
/// Returns a tree of [`ThreadNode`]s representing the threading
/// structure of matching messages.
///
/// `algorithm` is the threading algorithm (e.g. `"REFERENCES"`,
/// `"ORDEREDSUBJECT"`). Requires the server to advertise
/// `THREAD=<algorithm>` (RFC 5256 Section 1).
pub async fn thread(
&self,
algorithm: &str,
charset: &str,
criteria: impl AsRef<str>,
timeout: Duration,
) -> Result<Vec<ThreadNode>, Error> {
let criteria = criteria.as_ref();
self.check_utf8_only_enforced()?;
self.require_state(&[SessionState::Selected])?;
self.validate_search_criteria_capabilities(criteria)?;
{
let snap = self.state_rx.borrow();
if !snap
.capabilities
.contains(&Capability::Thread(algorithm.to_uppercase()))
{
return Err(Error::MissingCapability(format!(
"THREAD={}",
algorithm.to_uppercase()
)));
}
}
let cmd = Command::Thread {
algorithm: algorithm.to_owned(),
charset: charset.to_owned(),
criteria: criteria.to_owned(),
};
tokio::time::timeout(
timeout,
self.submit_regular(cmd, super::dispatch::ThreadConsumer::default()),
)
.await
.map_err(|_| Error::Timeout)?
}
/// UID THREAD — server-side threading returning UIDs
/// (RFC 5256 Section 3).
///
/// Like [`thread`](Self::thread) but returns UIDs instead of sequence
/// numbers in the [`ThreadNode`] tree.
///
/// Requires the server to advertise `THREAD=<algorithm>`
/// (RFC 5256 Section 1).
pub async fn uid_thread(
&self,
algorithm: &str,
charset: &str,
criteria: impl AsRef<str>,
timeout: Duration,
) -> Result<Vec<ThreadNode>, Error> {
let criteria = criteria.as_ref();
self.check_utf8_only_enforced()?;
self.require_state(&[SessionState::Selected])?;
self.validate_search_criteria_capabilities(criteria)?;
{
let snap = self.state_rx.borrow();
if !snap
.capabilities
.contains(&Capability::Thread(algorithm.to_uppercase()))
{
return Err(Error::MissingCapability(format!(
"THREAD={}",
algorithm.to_uppercase()
)));
}
}
let cmd = Command::UidThread {
algorithm: algorithm.to_owned(),
charset: charset.to_owned(),
criteria: criteria.to_owned(),
};
tokio::time::timeout(
timeout,
self.submit_regular(cmd, super::dispatch::ThreadConsumer::default()),
)
.await
.map_err(|_| Error::Timeout)?
}
}