1pub mod decode;
2pub mod encode;
3
4#[derive(Clone, Debug, Default, PartialEq)]
6#[non_exhaustive]
11pub struct GreetingCodec;
12
13#[derive(Clone, Debug, Default, PartialEq)]
15#[non_exhaustive]
16pub struct CommandCodec;
17
18#[derive(Clone, Debug, Default, PartialEq)]
20#[non_exhaustive]
21pub struct AuthenticateDataCodec;
22
23#[derive(Clone, Debug, Default, PartialEq)]
25#[non_exhaustive]
26pub struct ResponseCodec;
27
28#[derive(Clone, Debug, Default, PartialEq)]
30#[non_exhaustive]
31pub struct IdleDoneCodec;
32
33macro_rules! impl_codec_new {
34 ($codec:ty) => {
35 impl $codec {
36 pub fn new() -> Self {
38 Self::default()
39 }
40 }
41 };
42}
43
44impl_codec_new!(GreetingCodec);
45impl_codec_new!(CommandCodec);
46impl_codec_new!(AuthenticateDataCodec);
47impl_codec_new!(ResponseCodec);
48impl_codec_new!(IdleDoneCodec);
49
50#[cfg(test)]
51mod tests {
52 use std::num::NonZeroU32;
53
54 use imap_types::{
55 auth::AuthenticateData,
56 command::{Command, CommandBody},
57 core::{IString, Literal, LiteralMode, NString, Tag, Vec1},
58 extensions::idle::IdleDone,
59 fetch::MessageDataItem,
60 mailbox::Mailbox,
61 response::{Data, Greeting, GreetingKind, Response},
62 };
63
64 use super::*;
65 use crate::{
66 decode::{CommandDecodeError, Decoder, GreetingDecodeError, ResponseDecodeError},
67 testing::{
68 kat_inverse_authenticate_data, kat_inverse_command, kat_inverse_done,
69 kat_inverse_greeting, kat_inverse_response,
70 },
71 };
72
73 #[test]
74 fn test_kat_inverse_greeting() {
75 kat_inverse_greeting(&[
76 (
77 b"* OK ...\r\n".as_ref(),
78 b"".as_ref(),
79 Greeting::new(GreetingKind::Ok, None, "...").unwrap(),
80 ),
81 (
82 b"* ByE .\r\n???",
83 b"???",
84 Greeting::new(GreetingKind::Bye, None, ".").unwrap(),
85 ),
86 (
87 b"* preaUth x\r\n?",
88 b"?",
89 Greeting::new(GreetingKind::PreAuth, None, "x").unwrap(),
90 ),
91 ]);
92 }
93
94 #[test]
95 fn test_kat_inverse_command() {
96 kat_inverse_command(&[
97 (
98 b"a nOOP\r\n".as_ref(),
99 b"".as_ref(),
100 Command::new("a", CommandBody::Noop).unwrap(),
101 ),
102 (
103 b"a NooP\r\n???",
104 b"???",
105 Command::new("a", CommandBody::Noop).unwrap(),
106 ),
107 (
108 b"a SeLECT {5}\r\ninbox\r\n",
109 b"",
110 Command::new(
111 "a",
112 CommandBody::Select {
113 mailbox: Mailbox::Inbox,
114 #[cfg(feature = "ext_condstore_qresync")]
115 parameters: Vec::default(),
116 },
117 )
118 .unwrap(),
119 ),
120 (
121 b"a SElECT {5}\r\ninbox\r\nxxx",
122 b"xxx",
123 Command::new(
124 "a",
125 CommandBody::Select {
126 mailbox: Mailbox::Inbox,
127 #[cfg(feature = "ext_condstore_qresync")]
128 parameters: Vec::default(),
129 },
130 )
131 .unwrap(),
132 ),
133 ]);
134 }
135
136 #[test]
137 fn test_kat_inverse_response() {
138 kat_inverse_response(&[
139 (
140 b"* SEARCH 1\r\n".as_ref(),
141 b"".as_ref(),
142 Response::Data(Data::Search(
143 vec![NonZeroU32::new(1).unwrap()],
144 #[cfg(feature = "ext_condstore_qresync")]
145 None,
146 )),
147 ),
148 (
149 b"* SEARCH 1\r\n???",
150 b"???",
151 Response::Data(Data::Search(
152 vec![NonZeroU32::new(1).unwrap()],
153 #[cfg(feature = "ext_condstore_qresync")]
154 None,
155 )),
156 ),
157 (
158 b"* 1 FETCH (RFC822 {5}\r\nhello)\r\n",
159 b"",
160 Response::Data(Data::Fetch {
161 seq: NonZeroU32::new(1).unwrap(),
162 items: Vec1::from(MessageDataItem::Rfc822(NString(Some(IString::Literal(
163 Literal::try_from(b"hello".as_ref()).unwrap(),
164 ))))),
165 }),
166 ),
167 ]);
168 }
169
170 #[test]
171 fn test_kat_inverse_authenticate_data() {
172 kat_inverse_authenticate_data(&[
173 (
174 b"VGVzdA==\r\n".as_ref(),
175 b"".as_ref(),
176 AuthenticateData::r#continue(b"Test".to_vec()),
177 ),
178 (
179 b"AA==\r\n".as_ref(),
180 b"".as_ref(),
181 AuthenticateData::r#continue(b"\x00".to_vec()),
182 ),
183 (
184 b"aQ==\r\n".as_ref(),
185 b"".as_ref(),
186 AuthenticateData::r#continue(b"\x69".to_vec()),
187 ),
188 (b"*\r\n".as_ref(), b"".as_ref(), AuthenticateData::Cancel),
189 ]);
190 }
191
192 #[test]
193 fn test_kat_inverse_done() {
194 kat_inverse_done(&[
195 (b"done\r\n".as_ref(), b"".as_ref(), IdleDone),
196 (b"DONE\r\n".as_ref(), b"".as_ref(), IdleDone),
197 ]);
198 }
199
200 #[test]
201 fn test_greeting_incomplete_failed() {
202 let tests = [
203 (b"*".as_ref(), Err(GreetingDecodeError::Incomplete)),
205 (b"* ".as_ref(), Err(GreetingDecodeError::Incomplete)),
206 (b"* O".as_ref(), Err(GreetingDecodeError::Incomplete)),
207 (b"* OK".as_ref(), Err(GreetingDecodeError::Incomplete)),
208 (b"* OK ".as_ref(), Err(GreetingDecodeError::Incomplete)),
209 (b"* OK .".as_ref(), Err(GreetingDecodeError::Incomplete)),
210 (b"* OK .\r".as_ref(), Err(GreetingDecodeError::Incomplete)),
211 (b"**".as_ref(), Err(GreetingDecodeError::Failed)),
213 (b"* NO x\r\n".as_ref(), Err(GreetingDecodeError::Failed)),
214 ];
215
216 for (test, expected) in tests {
217 let got = GreetingCodec::default().decode(test);
218 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
219 assert_eq!(expected, got);
220
221 {
222 let got = GreetingCodec::default().decode_static(test);
223 assert_eq!(expected, got);
224 }
225 }
226 }
227
228 #[test]
229 fn test_command_incomplete_failed() {
230 let tests = [
231 (b"a".as_ref(), Err(CommandDecodeError::Incomplete)),
233 (b"a ".as_ref(), Err(CommandDecodeError::Incomplete)),
234 (b"a n".as_ref(), Err(CommandDecodeError::Incomplete)),
235 (b"a no".as_ref(), Err(CommandDecodeError::Incomplete)),
236 (b"a noo".as_ref(), Err(CommandDecodeError::Incomplete)),
237 (b"a noop".as_ref(), Err(CommandDecodeError::Incomplete)),
238 (b"a noop\r".as_ref(), Err(CommandDecodeError::Incomplete)),
239 (
241 b"a select {5}\r\n".as_ref(),
242 Err(CommandDecodeError::LiteralFound {
243 tag: Tag::try_from("a").unwrap(),
244 length: 5,
245 mode: LiteralMode::Sync,
246 }),
247 ),
248 (
249 b"a select {5+}\r\n".as_ref(),
250 Err(CommandDecodeError::LiteralFound {
251 tag: Tag::try_from("a").unwrap(),
252 length: 5,
253 mode: LiteralMode::NonSync,
254 }),
255 ),
256 (
258 b"a select {5}\r\nxxx".as_ref(),
259 Err(CommandDecodeError::Incomplete),
260 ),
261 (b"* noop\r\n".as_ref(), Err(CommandDecodeError::Failed)),
263 (b"A noop\r\n".as_ref(), Err(CommandDecodeError::Failed)),
264 ];
265
266 for (test, expected) in tests {
267 let got = CommandCodec::default().decode(test);
268 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
269 assert_eq!(expected, got);
270
271 {
272 let got = CommandCodec::default().decode_static(test);
273 assert_eq!(expected, got);
274 }
275 }
276 }
277
278 #[test]
279 fn test_response_incomplete_failed() {
280 let tests = [
281 (b"".as_ref(), Err(ResponseDecodeError::Incomplete)),
283 (b"*".as_ref(), Err(ResponseDecodeError::Incomplete)),
284 (b"* ".as_ref(), Err(ResponseDecodeError::Incomplete)),
285 (b"* S".as_ref(), Err(ResponseDecodeError::Incomplete)),
286 (b"* SE".as_ref(), Err(ResponseDecodeError::Incomplete)),
287 (b"* SEA".as_ref(), Err(ResponseDecodeError::Incomplete)),
288 (b"* SEAR".as_ref(), Err(ResponseDecodeError::Incomplete)),
289 (b"* SEARC".as_ref(), Err(ResponseDecodeError::Incomplete)),
290 (b"* SEARCH".as_ref(), Err(ResponseDecodeError::Incomplete)),
291 (b"* SEARCH ".as_ref(), Err(ResponseDecodeError::Incomplete)),
292 (b"* SEARCH 1".as_ref(), Err(ResponseDecodeError::Incomplete)),
293 (
294 b"* SEARCH 1\r".as_ref(),
295 Err(ResponseDecodeError::Incomplete),
296 ),
297 (
299 b"* 1 FETCH (RFC822 {5}\r\n".as_ref(),
300 Err(ResponseDecodeError::LiteralFound { length: 5 }),
301 ),
302 (
304 b"* search 1 2 3\r\n".as_ref(),
305 Err(ResponseDecodeError::Failed),
306 ),
307 (b"A search\r\n".as_ref(), Err(ResponseDecodeError::Failed)),
308 ];
309
310 for (test, expected) in tests {
311 let got = ResponseCodec::default().decode(test);
312 dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
313 assert_eq!(expected, got);
314
315 {
316 let got = ResponseCodec::default().decode_static(test);
317 assert_eq!(expected, got);
318 }
319 }
320 }
321}