Skip to main content

daaki_imap/connection/
sort_thread.rs

1#![allow(clippy::wildcard_imports)]
2use super::*;
3
4impl ImapConnection {
5    // -----------------------------------------------------------------------
6    // SORT (RFC 5256 Section 2)
7    // -----------------------------------------------------------------------
8
9    /// SORT — server-side sorting by sequence number (RFC 5256 Section 2).
10    ///
11    /// Returns matching sequence numbers sorted by the given sort keys,
12    /// along with an optional MODSEQ value (RFC 7162 Section 3.1.5).
13    ///
14    /// `sort_criteria` is a space-separated list of sort keys (e.g.
15    /// `"REVERSE DATE SUBJECT"`). The encoder wraps them in parentheses
16    /// per the SORT ABNF: `sort-criteria = "(" sort-key *(SP sort-key) ")"`.
17    ///
18    /// Requires the `SORT` capability (RFC 5256 Section 1).
19    pub async fn sort(
20        &self,
21        sort_criteria: &str,
22        charset: &str,
23        criteria: impl AsRef<str>,
24        timeout: Duration,
25    ) -> Result<SearchResult, Error> {
26        let criteria = criteria.as_ref();
27        self.check_utf8_only_enforced()?;
28        self.require_state(&[SessionState::Selected])?;
29        self.validate_search_criteria_capabilities(criteria)?;
30        {
31            let snap = self.state_rx.borrow();
32            if !snap.capabilities.contains(&Capability::Sort) {
33                return Err(Error::MissingCapability("SORT".into()));
34            }
35        }
36        let cmd = Command::Sort {
37            sort_criteria: sort_criteria.to_owned(),
38            charset: charset.to_owned(),
39            criteria: criteria.to_owned(),
40        };
41        tokio::time::timeout(
42            timeout,
43            self.submit_regular(cmd, super::dispatch::SortConsumer::default()),
44        )
45        .await
46        .map_err(|_| Error::Timeout)?
47    }
48
49    /// UID SORT — server-side sorting returning UIDs (RFC 5256 Section 2).
50    ///
51    /// Like [`sort`](Self::sort) but returns UIDs instead of sequence
52    /// numbers.
53    ///
54    /// Requires the `SORT` capability (RFC 5256 Section 1).
55    pub async fn uid_sort(
56        &self,
57        sort_criteria: &str,
58        charset: &str,
59        criteria: impl AsRef<str>,
60        timeout: Duration,
61    ) -> Result<SearchResult, Error> {
62        let criteria = criteria.as_ref();
63        self.check_utf8_only_enforced()?;
64        self.require_state(&[SessionState::Selected])?;
65        self.validate_search_criteria_capabilities(criteria)?;
66        {
67            let snap = self.state_rx.borrow();
68            if !snap.capabilities.contains(&Capability::Sort) {
69                return Err(Error::MissingCapability("SORT".into()));
70            }
71        }
72        let cmd = Command::UidSort {
73            sort_criteria: sort_criteria.to_owned(),
74            charset: charset.to_owned(),
75            criteria: criteria.to_owned(),
76        };
77        tokio::time::timeout(
78            timeout,
79            self.submit_regular(cmd, super::dispatch::SortConsumer::default()),
80        )
81        .await
82        .map_err(|_| Error::Timeout)?
83    }
84
85    // -----------------------------------------------------------------------
86    // THREAD (RFC 5256 Section 3)
87    // -----------------------------------------------------------------------
88
89    /// THREAD — server-side threading by sequence number
90    /// (RFC 5256 Section 3).
91    ///
92    /// Returns a tree of [`ThreadNode`]s representing the threading
93    /// structure of matching messages.
94    ///
95    /// `algorithm` is the threading algorithm (e.g. `"REFERENCES"`,
96    /// `"ORDEREDSUBJECT"`). Requires the server to advertise
97    /// `THREAD=<algorithm>` (RFC 5256 Section 1).
98    pub async fn thread(
99        &self,
100        algorithm: &str,
101        charset: &str,
102        criteria: impl AsRef<str>,
103        timeout: Duration,
104    ) -> Result<Vec<ThreadNode>, Error> {
105        let criteria = criteria.as_ref();
106        self.check_utf8_only_enforced()?;
107        self.require_state(&[SessionState::Selected])?;
108        self.validate_search_criteria_capabilities(criteria)?;
109        {
110            let snap = self.state_rx.borrow();
111            if !snap
112                .capabilities
113                .contains(&Capability::Thread(algorithm.to_uppercase()))
114            {
115                return Err(Error::MissingCapability(format!(
116                    "THREAD={}",
117                    algorithm.to_uppercase()
118                )));
119            }
120        }
121        let cmd = Command::Thread {
122            algorithm: algorithm.to_owned(),
123            charset: charset.to_owned(),
124            criteria: criteria.to_owned(),
125        };
126        tokio::time::timeout(
127            timeout,
128            self.submit_regular(cmd, super::dispatch::ThreadConsumer::default()),
129        )
130        .await
131        .map_err(|_| Error::Timeout)?
132    }
133
134    /// UID THREAD — server-side threading returning UIDs
135    /// (RFC 5256 Section 3).
136    ///
137    /// Like [`thread`](Self::thread) but returns UIDs instead of sequence
138    /// numbers in the [`ThreadNode`] tree.
139    ///
140    /// Requires the server to advertise `THREAD=<algorithm>`
141    /// (RFC 5256 Section 1).
142    pub async fn uid_thread(
143        &self,
144        algorithm: &str,
145        charset: &str,
146        criteria: impl AsRef<str>,
147        timeout: Duration,
148    ) -> Result<Vec<ThreadNode>, Error> {
149        let criteria = criteria.as_ref();
150        self.check_utf8_only_enforced()?;
151        self.require_state(&[SessionState::Selected])?;
152        self.validate_search_criteria_capabilities(criteria)?;
153        {
154            let snap = self.state_rx.borrow();
155            if !snap
156                .capabilities
157                .contains(&Capability::Thread(algorithm.to_uppercase()))
158            {
159                return Err(Error::MissingCapability(format!(
160                    "THREAD={}",
161                    algorithm.to_uppercase()
162                )));
163            }
164        }
165        let cmd = Command::UidThread {
166            algorithm: algorithm.to_owned(),
167            charset: charset.to_owned(),
168            criteria: criteria.to_owned(),
169        };
170        tokio::time::timeout(
171            timeout,
172            self.submit_regular(cmd, super::dispatch::ThreadConsumer::default()),
173        )
174        .await
175        .map_err(|_| Error::Timeout)?
176    }
177}