Skip to main content

imap_codec/codec/
decode.rs

1//! # Decoding of messages.
2//!
3//! You can use [`Decoder`]s to parse messages.
4//!
5//! IMAP literals make separating the parsing logic from the application logic difficult.
6//! When a server recognizes a literal (e.g. `{42}\r\n`) in a command, it first needs to agree to receive more data by sending a so-called "command continuation request" (`+ ...`).
7//! Without a command continuation request, a client won't send more data, and the command parser on the server would always return `LiteralFound { length: 42, .. }`.
8//! This makes real-world decoding of IMAP more elaborate.
9//!
10//! Have a look at the [parse_command](https://github.com/duesee/imap-codec/blob/main/imap-codec/examples/parse_command.rs) example to see how a real-world application could decode IMAP.
11
12use std::{
13    num::{ParseIntError, TryFromIntError},
14    str::Utf8Error,
15};
16
17use imap_types::{
18    IntoStatic,
19    auth::AuthenticateData,
20    command::Command,
21    core::{LiteralMode, Tag},
22    extensions::idle::IdleDone,
23    response::{Greeting, Response},
24};
25use nom::error::{ErrorKind, FromExternalError, ParseError};
26
27use crate::{
28    AuthenticateDataCodec, CommandCodec, GreetingCodec, IdleDoneCodec, ResponseCodec,
29    auth::authenticate_data,
30    command::command,
31    extensions::idle::idle_done,
32    response::{greeting, response},
33};
34
35/// An extended version of [`nom::IResult`].
36pub(crate) type IMAPResult<'a, I, O> = Result<(I, O), nom::Err<IMAPParseError<'a, I>>>;
37
38/// An extended version of [`nom::error::Error`].
39#[derive(Debug)]
40pub(crate) struct IMAPParseError<'a, I> {
41    #[allow(unused)]
42    pub input: I,
43    pub kind: IMAPErrorKind<'a>,
44}
45
46/// An extended version of [`nom::error::ErrorKind`].
47#[derive(Debug)]
48pub(crate) enum IMAPErrorKind<'a> {
49    Literal {
50        tag: Option<Tag<'a>>,
51        length: u32,
52        mode: LiteralMode,
53    },
54    BadNumber,
55    BadBase64,
56    BadUtf8,
57    BadDateTime,
58    LiteralContainsNull,
59    RecursionLimitExceeded,
60    Nom(#[allow(dead_code)] ErrorKind),
61}
62
63impl<I> ParseError<I> for IMAPParseError<'_, I> {
64    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
65        Self {
66            input,
67            kind: IMAPErrorKind::Nom(kind),
68        }
69    }
70
71    fn append(input: I, kind: ErrorKind, _: Self) -> Self {
72        Self {
73            input,
74            kind: IMAPErrorKind::Nom(kind),
75        }
76    }
77}
78
79impl<I> FromExternalError<I, ParseIntError> for IMAPParseError<'_, I> {
80    fn from_external_error(input: I, _: ErrorKind, _: ParseIntError) -> Self {
81        Self {
82            input,
83            kind: IMAPErrorKind::BadNumber,
84        }
85    }
86}
87
88impl<I> FromExternalError<I, TryFromIntError> for IMAPParseError<'_, I> {
89    fn from_external_error(input: I, _: ErrorKind, _: TryFromIntError) -> Self {
90        Self {
91            input,
92            kind: IMAPErrorKind::BadNumber,
93        }
94    }
95}
96
97impl<I> FromExternalError<I, base64::DecodeError> for IMAPParseError<'_, I> {
98    fn from_external_error(input: I, _: ErrorKind, _: base64::DecodeError) -> Self {
99        Self {
100            input,
101            kind: IMAPErrorKind::BadBase64,
102        }
103    }
104}
105
106impl<I> FromExternalError<I, Utf8Error> for IMAPParseError<'_, I> {
107    fn from_external_error(input: I, _: ErrorKind, _: Utf8Error) -> Self {
108        Self {
109            input,
110            kind: IMAPErrorKind::BadUtf8,
111        }
112    }
113}
114
115/// Decoder.
116///
117/// Implemented for types that know how to decode a specific IMAP message. See [implementors](trait.Decoder.html#implementors).
118pub trait Decoder {
119    type Message<'a>: Sized;
120    type Error<'a>;
121
122    fn decode<'a>(&self, input: &'a [u8])
123    -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'a>>;
124
125    fn decode_static<'a>(
126        &self,
127        input: &'a [u8],
128    ) -> Result<(&'a [u8], Self::Message<'static>), Self::Error<'static>>
129    where
130        Self::Message<'a>: IntoStatic<Static = Self::Message<'static>>,
131        Self::Error<'a>: IntoStatic<Static = Self::Error<'static>>,
132    {
133        let (remaining, value) = self.decode(input).map_err(IntoStatic::into_static)?;
134        Ok((remaining, value.into_static()))
135    }
136}
137
138/// Error during greeting decoding.
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140pub enum GreetingDecodeError {
141    /// More data is needed.
142    Incomplete,
143
144    /// Decoding failed.
145    Failed,
146}
147
148impl IntoStatic for GreetingDecodeError {
149    type Static = Self;
150
151    fn into_static(self) -> Self::Static {
152        self
153    }
154}
155
156/// Error during command decoding.
157#[derive(Clone, Debug, Eq, PartialEq)]
158pub enum CommandDecodeError<'a> {
159    /// More data is needed.
160    Incomplete,
161
162    /// More data is needed (and further action may be necessary).
163    ///
164    /// The decoder stopped at the beginning of literal data. Typically, a server MUST send a
165    /// command continuation request to agree to the receival of the remaining data. This behaviour
166    /// is different when `LITERAL+/LITERAL-` is used.
167    ///
168    /// # With `LITERAL+/LITERAL-`
169    ///
170    /// When the `mode` is sync, everything is the same as above.
171    ///
172    /// When the `mode` is non-sync, *and* the server advertised the LITERAL+ capability,
173    /// it MUST NOT send a command continuation request and accept the data right away.
174    ///
175    /// When the `mode` is non-sync, *and* the server advertised the LITERAL- capability,
176    /// *and* the literal length is smaller or equal than 4096,
177    /// it MUST NOT send a command continuation request and accept the data right away.
178    ///
179    /// When the `mode` is non-sync, *and* the server advertised the LITERAL- capability,
180    /// *and* the literal length is greater than 4096,
181    /// it MUST be handled as sync.
182    ///
183    /// ```rust,ignore
184    /// match mode {
185    ///     LiteralMode::Sync => /* Same as sync. */
186    ///     LiteralMode::Sync => match advertised {
187    ///         Capability::LiteralPlus => /* Accept data right away. */
188    ///         Capability::LiteralMinus => {
189    ///             if literal_length <= 4096 {
190    ///                 /* Accept data right away. */
191    ///             } else {
192    ///                 /* Same as sync. */
193    ///             }
194    ///         }
195    ///     }
196    /// }
197    /// ```
198    LiteralFound {
199        /// The corresponding command (tag) to which this literal is bound.
200        ///
201        /// This is required to reject literals, e.g., when their size exceeds a limit.
202        tag: Tag<'a>,
203
204        /// Literal length.
205        length: u32,
206
207        /// Literal mode, i.e., sync or non-sync.
208        mode: LiteralMode,
209    },
210
211    /// Decoding failed.
212    Failed,
213}
214
215impl IntoStatic for CommandDecodeError<'_> {
216    type Static = CommandDecodeError<'static>;
217
218    fn into_static(self) -> Self::Static {
219        match self {
220            CommandDecodeError::Incomplete => CommandDecodeError::Incomplete,
221            CommandDecodeError::LiteralFound { tag, length, mode } => {
222                CommandDecodeError::LiteralFound {
223                    tag: tag.into_static(),
224                    length,
225                    mode,
226                }
227            }
228            CommandDecodeError::Failed => CommandDecodeError::Failed,
229        }
230    }
231}
232
233/// Error during authenticate data line decoding.
234#[derive(Clone, Copy, Debug, Eq, PartialEq)]
235pub enum AuthenticateDataDecodeError {
236    /// More data is needed.
237    Incomplete,
238
239    /// Decoding failed.
240    Failed,
241}
242
243impl IntoStatic for AuthenticateDataDecodeError {
244    type Static = Self;
245
246    fn into_static(self) -> Self::Static {
247        self
248    }
249}
250
251/// Error during response decoding.
252#[derive(Clone, Debug, Eq, PartialEq)]
253pub enum ResponseDecodeError {
254    /// More data is needed.
255    Incomplete,
256
257    /// The decoder stopped at the beginning of literal data.
258    ///
259    /// The client *MUST* accept the literal and has no option to reject it.
260    /// However, when the client ultimately does not want to handle the literal, it can do something
261    /// similar to <https://datatracker.ietf.org/doc/html/rfc7888#section-4>.
262    ///
263    /// It can implement a discarding mechanism, basically, consuming the whole literal but not
264    /// saving the bytes in memory. Or, it can close the connection.
265    LiteralFound {
266        /// Literal length.
267        length: u32,
268    },
269
270    /// Decoding failed.
271    Failed,
272}
273
274impl IntoStatic for ResponseDecodeError {
275    type Static = Self;
276
277    fn into_static(self) -> Self::Static {
278        self
279    }
280}
281
282/// Error during idle done decoding.
283#[derive(Clone, Copy, Debug, Eq, PartialEq)]
284pub enum IdleDoneDecodeError {
285    /// More data is needed.
286    Incomplete,
287
288    /// Decoding failed.
289    Failed,
290}
291
292impl IntoStatic for IdleDoneDecodeError {
293    type Static = Self;
294
295    fn into_static(self) -> Self::Static {
296        self
297    }
298}
299
300// -------------------------------------------------------------------------------------------------
301
302impl Decoder for GreetingCodec {
303    type Message<'a> = Greeting<'a>;
304    type Error<'a> = GreetingDecodeError;
305
306    fn decode<'a>(
307        &self,
308        input: &'a [u8],
309    ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
310        match greeting(input) {
311            Ok((rem, grt)) => Ok((rem, grt)),
312            Err(nom::Err::Incomplete(_)) => Err(GreetingDecodeError::Incomplete),
313            Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err(GreetingDecodeError::Failed),
314        }
315    }
316}
317
318impl Decoder for CommandCodec {
319    type Message<'a> = Command<'a>;
320    type Error<'a> = CommandDecodeError<'a>;
321
322    fn decode<'a>(
323        &self,
324        input: &'a [u8],
325    ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'a>> {
326        match command(input) {
327            Ok((rem, cmd)) => Ok((rem, cmd)),
328            Err(nom::Err::Incomplete(_)) => Err(CommandDecodeError::Incomplete),
329            Err(nom::Err::Failure(error)) => match error {
330                IMAPParseError {
331                    input: _,
332                    kind: IMAPErrorKind::Literal { tag, length, mode },
333                } => Err(CommandDecodeError::LiteralFound {
334                    // Unwrap: We *must* receive a `tag` during command parsing.
335                    tag: tag.expect("Expected `Some(tag)` in `IMAPErrorKind::Literal`, got `None`"),
336                    length,
337                    mode,
338                }),
339                _ => Err(CommandDecodeError::Failed),
340            },
341            Err(nom::Err::Error(_)) => Err(CommandDecodeError::Failed),
342        }
343    }
344}
345
346impl Decoder for ResponseCodec {
347    type Message<'a> = Response<'a>;
348    type Error<'a> = ResponseDecodeError;
349
350    fn decode<'a>(
351        &self,
352        input: &'a [u8],
353    ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
354        match response(input) {
355            Ok((rem, rsp)) => Ok((rem, rsp)),
356            Err(nom::Err::Incomplete(_)) => Err(ResponseDecodeError::Incomplete),
357            Err(nom::Err::Error(error) | nom::Err::Failure(error)) => match error {
358                IMAPParseError {
359                    kind: IMAPErrorKind::Literal { length, .. },
360                    ..
361                } => Err(ResponseDecodeError::LiteralFound { length }),
362                _ => Err(ResponseDecodeError::Failed),
363            },
364        }
365    }
366}
367
368impl Decoder for AuthenticateDataCodec {
369    type Message<'a> = AuthenticateData<'a>;
370    type Error<'a> = AuthenticateDataDecodeError;
371
372    fn decode<'a>(
373        &self,
374        input: &'a [u8],
375    ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
376        match authenticate_data(input) {
377            Ok((rem, rsp)) => Ok((rem, rsp)),
378            Err(nom::Err::Incomplete(_)) => Err(AuthenticateDataDecodeError::Incomplete),
379            Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => {
380                Err(AuthenticateDataDecodeError::Failed)
381            }
382        }
383    }
384}
385
386impl Decoder for IdleDoneCodec {
387    type Message<'a> = IdleDone;
388    type Error<'a> = IdleDoneDecodeError;
389
390    fn decode<'a>(
391        &self,
392        input: &'a [u8],
393    ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> {
394        match idle_done(input) {
395            Ok((rem, rsp)) => Ok((rem, rsp)),
396            Err(nom::Err::Incomplete(_)) => Err(IdleDoneDecodeError::Incomplete),
397            Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err(IdleDoneDecodeError::Failed),
398        }
399    }
400}
401
402#[cfg(test)]
403mod tests {
404    use std::num::NonZeroU32;
405
406    use imap_types::{
407        command::{Command, CommandBody},
408        core::{IString, Literal, NString, Vec1},
409        extensions::idle::IdleDone,
410        fetch::MessageDataItem,
411        mailbox::Mailbox,
412        response::{Data, Greeting, GreetingKind, Response},
413    };
414
415    use super::*;
416
417    #[test]
418    fn test_decode_greeting() {
419        let tests = [
420            // Ok
421            (
422                b"* OK ...\r\n".as_ref(),
423                Ok((
424                    b"".as_ref(),
425                    Greeting::new(GreetingKind::Ok, None, "...").unwrap(),
426                )),
427            ),
428            (
429                b"* ByE .\r\n???".as_ref(),
430                Ok((
431                    b"???".as_ref(),
432                    Greeting::new(GreetingKind::Bye, None, ".").unwrap(),
433                )),
434            ),
435            (
436                b"* preaUth x\r\n?".as_ref(),
437                Ok((
438                    b"?".as_ref(),
439                    Greeting::new(GreetingKind::PreAuth, None, "x").unwrap(),
440                )),
441            ),
442            // Incomplete
443            (b"*".as_ref(), Err(GreetingDecodeError::Incomplete)),
444            (b"* ".as_ref(), Err(GreetingDecodeError::Incomplete)),
445            (b"* O".as_ref(), Err(GreetingDecodeError::Incomplete)),
446            (b"* OK".as_ref(), Err(GreetingDecodeError::Incomplete)),
447            (b"* OK ".as_ref(), Err(GreetingDecodeError::Incomplete)),
448            (b"* OK .".as_ref(), Err(GreetingDecodeError::Incomplete)),
449            (b"* OK .\r".as_ref(), Err(GreetingDecodeError::Incomplete)),
450            // Failed
451            (b"**".as_ref(), Err(GreetingDecodeError::Failed)),
452            (b"* NO x\r\n".as_ref(), Err(GreetingDecodeError::Failed)),
453        ];
454
455        for (test, expected) in tests {
456            let got = GreetingCodec::default().decode(test);
457            dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
458            assert_eq!(expected, got);
459
460            {
461                let got = GreetingCodec::default().decode_static(test);
462                assert_eq!(expected, got);
463            }
464        }
465    }
466
467    #[test]
468    fn test_decode_command() {
469        let tests = [
470            // Ok
471            (
472                b"a noop\r\n".as_ref(),
473                Ok((b"".as_ref(), Command::new("a", CommandBody::Noop).unwrap())),
474            ),
475            (
476                b"a noop\r\n???".as_ref(),
477                Ok((
478                    b"???".as_ref(),
479                    Command::new("a", CommandBody::Noop).unwrap(),
480                )),
481            ),
482            (
483                b"a select {5}\r\ninbox\r\n".as_ref(),
484                Ok((
485                    b"".as_ref(),
486                    Command::new(
487                        "a",
488                        CommandBody::Select {
489                            mailbox: Mailbox::Inbox,
490                            #[cfg(feature = "ext_condstore_qresync")]
491                            parameters: Vec::default(),
492                        },
493                    )
494                    .unwrap(),
495                )),
496            ),
497            (
498                b"a select {5}\r\ninbox\r\nxxx".as_ref(),
499                Ok((
500                    b"xxx".as_ref(),
501                    Command::new(
502                        "a",
503                        CommandBody::Select {
504                            mailbox: Mailbox::Inbox,
505                            #[cfg(feature = "ext_condstore_qresync")]
506                            parameters: Vec::default(),
507                        },
508                    )
509                    .unwrap(),
510                )),
511            ),
512            // Incomplete
513            (b"a".as_ref(), Err(CommandDecodeError::Incomplete)),
514            (b"a ".as_ref(), Err(CommandDecodeError::Incomplete)),
515            (b"a n".as_ref(), Err(CommandDecodeError::Incomplete)),
516            (b"a no".as_ref(), Err(CommandDecodeError::Incomplete)),
517            (b"a noo".as_ref(), Err(CommandDecodeError::Incomplete)),
518            (b"a noop".as_ref(), Err(CommandDecodeError::Incomplete)),
519            (b"a noop\r".as_ref(), Err(CommandDecodeError::Incomplete)),
520            // LiteralAckRequired
521            (
522                b"a select {5}\r\n".as_ref(),
523                Err(CommandDecodeError::LiteralFound {
524                    tag: Tag::try_from("a").unwrap(),
525                    length: 5,
526                    mode: LiteralMode::Sync,
527                }),
528            ),
529            // Incomplete (after literal)
530            (
531                b"a select {5}\r\nxxx".as_ref(),
532                Err(CommandDecodeError::Incomplete),
533            ),
534            // Failed
535            (b"* noop\r\n".as_ref(), Err(CommandDecodeError::Failed)),
536            (b"A  noop\r\n".as_ref(), Err(CommandDecodeError::Failed)),
537        ];
538
539        for (test, expected) in tests {
540            let got = CommandCodec::default().decode(test);
541            dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
542            assert_eq!(expected, got);
543
544            {
545                let got = CommandCodec::default().decode_static(test);
546                assert_eq!(expected, got);
547            }
548        }
549    }
550
551    #[test]
552    fn test_decode_authenticate_data() {
553        let tests = [
554            // Ok
555            (
556                b"VGVzdA==\r\n".as_ref(),
557                Ok((b"".as_ref(), AuthenticateData::r#continue(b"Test".to_vec()))),
558            ),
559            (
560                b"VGVzdA==\r\nx".as_ref(),
561                Ok((
562                    b"x".as_ref(),
563                    AuthenticateData::r#continue(b"Test".to_vec()),
564                )),
565            ),
566            (
567                b"*\r\n".as_ref(),
568                Ok((b"".as_ref(), AuthenticateData::Cancel)),
569            ),
570            (
571                b"*\r\nx".as_ref(),
572                Ok((b"x".as_ref(), AuthenticateData::Cancel)),
573            ),
574            // Incomplete
575            (b"V".as_ref(), Err(AuthenticateDataDecodeError::Incomplete)),
576            (b"VG".as_ref(), Err(AuthenticateDataDecodeError::Incomplete)),
577            (
578                b"VGV".as_ref(),
579                Err(AuthenticateDataDecodeError::Incomplete),
580            ),
581            (
582                b"VGVz".as_ref(),
583                Err(AuthenticateDataDecodeError::Incomplete),
584            ),
585            (
586                b"VGVzd".as_ref(),
587                Err(AuthenticateDataDecodeError::Incomplete),
588            ),
589            (
590                b"VGVzdA".as_ref(),
591                Err(AuthenticateDataDecodeError::Incomplete),
592            ),
593            (
594                b"VGVzdA=".as_ref(),
595                Err(AuthenticateDataDecodeError::Incomplete),
596            ),
597            (
598                b"VGVzdA==".as_ref(),
599                Err(AuthenticateDataDecodeError::Incomplete),
600            ),
601            (
602                b"VGVzdA==\r".as_ref(),
603                Err(AuthenticateDataDecodeError::Incomplete),
604            ),
605            (
606                b"VGVzdA==\r\n".as_ref(),
607                Ok((b"".as_ref(), AuthenticateData::r#continue(b"Test".to_vec()))),
608            ),
609            // Failed
610            (
611                b"VGVzdA== \r\n".as_ref(),
612                Err(AuthenticateDataDecodeError::Failed),
613            ),
614            (
615                b" VGVzdA== \r\n".as_ref(),
616                Err(AuthenticateDataDecodeError::Failed),
617            ),
618            (
619                b" V GVzdA== \r\n".as_ref(),
620                Err(AuthenticateDataDecodeError::Failed),
621            ),
622            (
623                b" V GVzdA= \r\n".as_ref(),
624                Err(AuthenticateDataDecodeError::Failed),
625            ),
626        ];
627
628        for (test, expected) in tests {
629            let got = AuthenticateDataCodec::default().decode(test);
630            dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
631            assert_eq!(expected, got);
632
633            {
634                let got = AuthenticateDataCodec::default().decode_static(test);
635                assert_eq!(expected, got);
636            }
637        }
638    }
639
640    #[test]
641    fn test_decode_idle_done() {
642        let tests = [
643            // Ok
644            (b"done\r\n".as_ref(), Ok((b"".as_ref(), IdleDone))),
645            (b"done\r\n?".as_ref(), Ok((b"?".as_ref(), IdleDone))),
646            // Incomplete
647            (b"d".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
648            (b"do".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
649            (b"don".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
650            (b"done".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
651            (b"done\r".as_ref(), Err(IdleDoneDecodeError::Incomplete)),
652            // Failed
653            (b"donee\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
654            (b" done\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
655            (b"done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
656            (b" done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)),
657        ];
658
659        for (test, expected) in tests {
660            let got = IdleDoneCodec::default().decode(test);
661            dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
662            assert_eq!(expected, got);
663
664            {
665                let got = IdleDoneCodec::default().decode_static(test);
666                assert_eq!(expected, got);
667            }
668        }
669    }
670
671    #[test]
672    fn test_decode_response() {
673        let tests = [
674            // Incomplete
675            (b"".as_ref(), Err(ResponseDecodeError::Incomplete)),
676            (b"*".as_ref(), Err(ResponseDecodeError::Incomplete)),
677            (b"* ".as_ref(), Err(ResponseDecodeError::Incomplete)),
678            (b"* S".as_ref(), Err(ResponseDecodeError::Incomplete)),
679            (b"* SE".as_ref(), Err(ResponseDecodeError::Incomplete)),
680            (b"* SEA".as_ref(), Err(ResponseDecodeError::Incomplete)),
681            (b"* SEAR".as_ref(), Err(ResponseDecodeError::Incomplete)),
682            (b"* SEARC".as_ref(), Err(ResponseDecodeError::Incomplete)),
683            (b"* SEARCH".as_ref(), Err(ResponseDecodeError::Incomplete)),
684            (b"* SEARCH ".as_ref(), Err(ResponseDecodeError::Incomplete)),
685            (b"* SEARCH 1".as_ref(), Err(ResponseDecodeError::Incomplete)),
686            (
687                b"* SEARCH 1\r".as_ref(),
688                Err(ResponseDecodeError::Incomplete),
689            ),
690            // Ok
691            (
692                b"* SEARCH 1\r\n".as_ref(),
693                Ok((
694                    b"".as_ref(),
695                    Response::Data(Data::Search(
696                        vec![NonZeroU32::new(1).unwrap()],
697                        #[cfg(feature = "ext_condstore_qresync")]
698                        None,
699                    )),
700                )),
701            ),
702            #[cfg(feature = "quirk_trailing_space_search")]
703            (
704                b"* SEARCH \r\n".as_ref(),
705                Ok((
706                    b"".as_ref(),
707                    Response::Data(Data::Search(
708                        vec![],
709                        #[cfg(feature = "ext_condstore_qresync")]
710                        None,
711                    )),
712                )),
713            ),
714            (
715                b"* SEARCH 1\r\n???".as_ref(),
716                Ok((
717                    b"???".as_ref(),
718                    Response::Data(Data::Search(
719                        vec![NonZeroU32::new(1).unwrap()],
720                        #[cfg(feature = "ext_condstore_qresync")]
721                        None,
722                    )),
723                )),
724            ),
725            (
726                b"* 1 FETCH (RFC822 {5}\r\nhello)\r\n".as_ref(),
727                Ok((
728                    b"".as_ref(),
729                    Response::Data(Data::Fetch {
730                        seq: NonZeroU32::new(1).unwrap(),
731                        items: Vec1::from(MessageDataItem::Rfc822(NString(Some(
732                            IString::Literal(Literal::try_from(b"hello".as_ref()).unwrap()),
733                        )))),
734                    }),
735                )),
736            ),
737            (
738                b"* 1 FETCH (RFC822 {5}\r\n".as_ref(),
739                Err(ResponseDecodeError::LiteralFound { length: 5 }),
740            ),
741            // Failed
742            (
743                b"*  search 1 2 3\r\n".as_ref(),
744                Err(ResponseDecodeError::Failed),
745            ),
746            #[cfg(not(feature = "quirk_trailing_space_search"))]
747            (b"* search \r\n".as_ref(), Err(ResponseDecodeError::Failed)),
748            (b"A search\r\n".as_ref(), Err(ResponseDecodeError::Failed)),
749        ];
750
751        for (test, expected) in tests {
752            let got = ResponseCodec::default().decode(test);
753            dbg!((std::str::from_utf8(test).unwrap(), &expected, &got));
754            assert_eq!(expected, got);
755
756            {
757                let got = ResponseCodec::default().decode_static(test);
758                assert_eq!(expected, got);
759            }
760        }
761    }
762}