Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{Flag, Sequence, quoted_string, serialize_sequence};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Arguments {
|
||||
pub tag: String,
|
||||
pub is_esearch: bool,
|
||||
pub sort: Option<Vec<Comparator>>,
|
||||
pub result_options: Vec<ResultOption>,
|
||||
pub filter: Vec<Filter>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Sort {
|
||||
Arrival,
|
||||
Cc,
|
||||
Date,
|
||||
From,
|
||||
DisplayFrom,
|
||||
Size,
|
||||
Subject,
|
||||
To,
|
||||
DisplayTo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Comparator {
|
||||
pub sort: Sort,
|
||||
pub ascending: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Response {
|
||||
pub is_uid: bool,
|
||||
pub is_esearch: bool,
|
||||
pub is_sort: bool,
|
||||
pub ids: Vec<u32>,
|
||||
pub min: Option<u32>,
|
||||
pub max: Option<u32>,
|
||||
pub count: Option<u32>,
|
||||
pub highest_modseq: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ResultOption {
|
||||
Min,
|
||||
Max,
|
||||
All,
|
||||
Count,
|
||||
Save,
|
||||
Context,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Filter {
|
||||
Sequence(Sequence, bool),
|
||||
All,
|
||||
Answered,
|
||||
Bcc(String),
|
||||
Before(i64),
|
||||
Body(String),
|
||||
Cc(String),
|
||||
Deleted,
|
||||
Draft,
|
||||
Flagged,
|
||||
From(String),
|
||||
Header(String, String),
|
||||
Keyword(Flag),
|
||||
Larger(u32),
|
||||
On(i64),
|
||||
Seen,
|
||||
SentBefore(i64),
|
||||
SentOn(i64),
|
||||
SentSince(i64),
|
||||
Since(i64),
|
||||
Smaller(u32),
|
||||
Subject(String),
|
||||
Text(String),
|
||||
To(String),
|
||||
Unanswered,
|
||||
Undeleted,
|
||||
Undraft,
|
||||
Unflagged,
|
||||
Unkeyword(Flag),
|
||||
Unseen,
|
||||
|
||||
// Logical operators
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
End,
|
||||
|
||||
// Imap4rev1
|
||||
Recent,
|
||||
New,
|
||||
Old,
|
||||
|
||||
// RFC 5032 - WITHIN
|
||||
Older(u32),
|
||||
Younger(u32),
|
||||
|
||||
// RFC 4551 - CONDSTORE
|
||||
ModSeq((u64, ModSeqEntry)),
|
||||
|
||||
// RFC 8474 - ObjectID
|
||||
EmailId(String),
|
||||
ThreadId(String),
|
||||
|
||||
// RFC 9738 - MESSAGELIMIT
|
||||
UidAfter(u32),
|
||||
UidBefore(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ModSeqEntry {
|
||||
Shared(Flag),
|
||||
Private(Flag),
|
||||
All(Flag),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn seq_saved_search() -> Filter {
|
||||
Filter::Sequence(Sequence::SavedSearch, false)
|
||||
}
|
||||
|
||||
pub fn seq_range(start: Option<u32>, end: Option<u32>) -> Filter {
|
||||
Filter::Sequence(Sequence::Range { start, end }, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn serialize(self, tag: &str) -> Vec<u8> {
|
||||
let mut buf = Vec::with_capacity(64);
|
||||
if self.is_esearch {
|
||||
buf.extend_from_slice(b"* ESEARCH (TAG ");
|
||||
quoted_string(&mut buf, tag);
|
||||
buf.extend_from_slice(b")");
|
||||
if self.is_uid {
|
||||
buf.extend_from_slice(b" UID");
|
||||
}
|
||||
if let Some(count) = &self.count {
|
||||
buf.extend_from_slice(b" COUNT ");
|
||||
buf.extend_from_slice(count.to_string().as_bytes());
|
||||
}
|
||||
if let Some(min) = &self.min {
|
||||
buf.extend_from_slice(b" MIN ");
|
||||
buf.extend_from_slice(min.to_string().as_bytes());
|
||||
}
|
||||
if let Some(max) = &self.max {
|
||||
buf.extend_from_slice(b" MAX ");
|
||||
buf.extend_from_slice(max.to_string().as_bytes());
|
||||
}
|
||||
if !self.ids.is_empty() {
|
||||
buf.extend_from_slice(b" ALL ");
|
||||
serialize_sequence(&mut buf, &self.ids);
|
||||
}
|
||||
if let Some(highest_modseq) = self.highest_modseq {
|
||||
buf.extend_from_slice(b" MODSEQ ");
|
||||
buf.extend_from_slice(highest_modseq.to_string().as_bytes());
|
||||
}
|
||||
} else {
|
||||
if !self.is_sort {
|
||||
buf.extend_from_slice(b"* SEARCH");
|
||||
} else {
|
||||
buf.extend_from_slice(b"* SORT");
|
||||
}
|
||||
if !self.ids.is_empty() {
|
||||
for id in &self.ids {
|
||||
buf.push(b' ');
|
||||
buf.extend_from_slice(id.to_string().as_bytes());
|
||||
}
|
||||
}
|
||||
if let Some(highest_modseq) = self.highest_modseq {
|
||||
buf.extend_from_slice(b" (MODSEQ ");
|
||||
buf.extend_from_slice(highest_modseq.to_string().as_bytes());
|
||||
buf.push(b')');
|
||||
}
|
||||
}
|
||||
buf.extend_from_slice(b"\r\n");
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn serialize_search() {
|
||||
for (mut response, tag, expected_v2, expected_v1) in [
|
||||
(
|
||||
super::Response {
|
||||
is_uid: false,
|
||||
is_esearch: true,
|
||||
is_sort: false,
|
||||
ids: vec![2, 10, 11],
|
||||
min: 2.into(),
|
||||
max: 11.into(),
|
||||
count: 3.into(),
|
||||
highest_modseq: None,
|
||||
},
|
||||
"A283",
|
||||
"* ESEARCH (TAG \"A283\") COUNT 3 MIN 2 MAX 11 ALL 2,10:11\r\n",
|
||||
"* SEARCH 2 10 11\r\n",
|
||||
),
|
||||
(
|
||||
super::Response {
|
||||
is_uid: false,
|
||||
is_esearch: true,
|
||||
is_sort: false,
|
||||
ids: vec![
|
||||
1, 2, 3, 5, 10, 11, 12, 13, 90, 92, 93, 94, 95, 96, 97, 98, 99,
|
||||
],
|
||||
min: None,
|
||||
max: None,
|
||||
count: None,
|
||||
highest_modseq: None,
|
||||
},
|
||||
"A283",
|
||||
"* ESEARCH (TAG \"A283\") ALL 1:3,5,10:13,90,92:99\r\n",
|
||||
"* SEARCH 1 2 3 5 10 11 12 13 90 92 93 94 95 96 97 98 99\r\n",
|
||||
),
|
||||
(
|
||||
super::Response {
|
||||
is_uid: false,
|
||||
is_esearch: true,
|
||||
is_sort: false,
|
||||
ids: vec![],
|
||||
min: None,
|
||||
max: None,
|
||||
count: None,
|
||||
highest_modseq: None,
|
||||
},
|
||||
"A283",
|
||||
"* ESEARCH (TAG \"A283\")\r\n",
|
||||
"* SEARCH\r\n",
|
||||
),
|
||||
(
|
||||
super::Response {
|
||||
is_uid: false,
|
||||
is_esearch: true,
|
||||
is_sort: false,
|
||||
ids: vec![10, 11, 12, 13, 21],
|
||||
min: None,
|
||||
max: None,
|
||||
count: None,
|
||||
highest_modseq: 12345.into(),
|
||||
},
|
||||
"A283",
|
||||
"* ESEARCH (TAG \"A283\") ALL 10:13,21 MODSEQ 12345\r\n",
|
||||
"* SEARCH 10 11 12 13 21 (MODSEQ 12345)\r\n",
|
||||
),
|
||||
] {
|
||||
let response_v2 = String::from_utf8(response.clone().serialize(tag)).unwrap();
|
||||
response.is_esearch = false;
|
||||
let response_v1 = String::from_utf8(response.serialize(tag)).unwrap();
|
||||
|
||||
assert_eq!(response_v2, expected_v2);
|
||||
assert_eq!(response_v1, expected_v1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user