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
//!
//! https://tools.ietf.org/html/rfc4315
//!
//! The IMAP UIDPLUS Extension
//!

use nom::{
    branch::alt,
    bytes::streaming::{tag, tag_no_case},
    combinator::map,
    multi::separated_list1,
    sequence::{preceded, tuple},
    IResult,
};

use crate::parser::core::number;
use crate::types::*;

/// Extends resp-text-code as follows:
///
/// ```ignore
///     resp-text-code =/ resp-code-apnd
///     resp-code-apnd = "APPENDUID" SP nz-number SP append-uid
///     append-uid      =/ uid-set
///                       ; only permitted if client uses [MULTIAPPEND]
///                       ; to append multiple messages.
/// ```
///
/// [RFC4315 - 3 Additional Response Codes](https://tools.ietf.org/html/rfc4315#section-3)
pub(crate) fn resp_text_code_append_uid(i: &[u8]) -> IResult<&[u8], ResponseCode> {
    map(
        preceded(
            tag_no_case(b"APPENDUID "),
            tuple((number, tag(" "), uid_set)),
        ),
        |(fst, _, snd)| ResponseCode::AppendUid(fst, snd),
    )(i)
}

/// Extends resp-text-code as follows:
///
/// ```ignore
///     resp-text-code =/ resp-code-copy
///     resp-code-copy = "COPYUID" SP nz-number SP uid-set
/// ```
///
/// [RFC4315 - 3 Additional Response Codes](https://tools.ietf.org/html/rfc4315#section-3)
pub(crate) fn resp_text_code_copy_uid(i: &[u8]) -> IResult<&[u8], ResponseCode> {
    map(
        preceded(
            tag_no_case(b"COPYUID "),
            tuple((number, tag(" "), uid_set, tag(" "), uid_set)),
        ),
        |(fst, _, snd, _, trd)| ResponseCode::CopyUid(fst, snd, trd),
    )(i)
}

/// Extends resp-text-code as follows:
///
/// ```ignore
///     resp-text-code =/ "UIDNOTSTICKY"
/// ```
///
/// [RFC4315 - 3 Additional Response Codes](https://tools.ietf.org/html/rfc4315#section-3)
pub(crate) fn resp_text_code_uid_not_sticky(i: &[u8]) -> IResult<&[u8], ResponseCode> {
    map(tag_no_case(b"UIDNOTSTICKY"), |_| ResponseCode::UidNotSticky)(i)
}

/// Parses the uid-set nonterminal:
///
/// ```ignore
///     uid-set = (uniqueid / uid-range) *("," uid-set)
/// ```
///
/// [RFC4315 - 4 Formal Syntax](https://tools.ietf.org/html/rfc4315#section-4)
fn uid_set(i: &[u8]) -> IResult<&[u8], Vec<UidSetMember>> {
    separated_list1(tag(","), alt((uid_range, map(number, From::from))))(i)
}

/// Parses the uid-set nonterminal:
///
/// ```ignore
///    uid-range = (uniqueid ":" uniqueid)
///                ; two uniqueid values and all values
///                ; between these two regards of order.
///                ; Example: 2:4 and 4:2 are equivalent.
/// ```
///
/// [RFC4315 - 4 Formal Syntax](https://tools.ietf.org/html/rfc4315#section-4)
fn uid_range(i: &[u8]) -> IResult<&[u8], UidSetMember> {
    map(
        nom::sequence::separated_pair(number, tag(":"), number),
        |(fst, snd)| if fst <= snd { fst..=snd } else { snd..=fst }.into(),
    )(i)
}