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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use std::convert::TryFrom;
use abnf_core::streaming::{DQUOTE, SP};
use nom::{
branch::alt,
bytes::streaming::{tag, tag_no_case, take_while1},
combinator::{map, opt, value},
multi::many0,
sequence::{delimited, preceded, tuple},
IResult,
};
use crate::{
parse::{
core::{
astring, is_atom_char, is_resp_specials, nil, number, nz_number, quoted_char, string,
},
flag::{flag_list, mbx_list_flags},
status::status_att_list,
},
types::{
flag::FlagNameAttribute,
mailbox::{ListCharString, ListMailbox, Mailbox, MailboxOther},
response::Data,
},
};
pub(crate) fn list_mailbox(input: &[u8]) -> IResult<&[u8], ListMailbox> {
alt((
map(take_while1(is_list_char), |bytes: &[u8]| {
ListMailbox::Token(
ListCharString::try_from(unsafe { String::from_utf8_unchecked(bytes.to_vec()) })
.unwrap(),
)
}),
map(string, |istr| ListMailbox::String(istr.to_owned())),
))(input)
}
pub(crate) fn is_list_char(i: u8) -> bool {
is_atom_char(i) || is_list_wildcards(i) || is_resp_specials(i)
}
pub(crate) fn is_list_wildcards(i: u8) -> bool {
i == b'%' || i == b'*'
}
pub(crate) fn mailbox(input: &[u8]) -> IResult<&[u8], Mailbox> {
map(astring, |astr| {
match MailboxOther::try_from(astr.to_owned()) {
Ok(other) => Mailbox::Other(other),
Err(_) => Mailbox::Inbox,
}
})(input)
}
pub(crate) fn mailbox_data(input: &[u8]) -> IResult<&[u8], Data> {
alt((
map(
tuple((tag_no_case(b"FLAGS"), SP, flag_list)),
|(_, _, flags)| Data::Flags(flags),
),
map(
tuple((tag_no_case(b"LIST"), SP, mailbox_list)),
|(_, _, (items, delimiter, mailbox))| Data::List {
items: items.unwrap_or_default(),
mailbox,
delimiter,
},
),
map(
tuple((tag_no_case(b"LSUB"), SP, mailbox_list)),
|(_, _, (items, delimiter, mailbox))| Data::Lsub {
items: items.unwrap_or_default(),
mailbox,
delimiter,
},
),
map(
tuple((tag_no_case(b"SEARCH"), many0(preceded(SP, nz_number)))),
|(_, nums)| Data::Search(nums),
),
map(
tuple((
tag_no_case(b"STATUS"),
SP,
mailbox,
SP,
delimited(tag(b"("), opt(status_att_list), tag(b")")),
)),
|(_, _, mailbox, _, attributes)| Data::Status {
mailbox,
attributes: attributes.unwrap_or_default(),
},
),
map(
tuple((number, SP, tag_no_case(b"EXISTS"))),
|(num, _, _)| Data::Exists(num),
),
map(
tuple((number, SP, tag_no_case(b"RECENT"))),
|(num, _, _)| Data::Recent(num),
),
))(input)
}
fn mailbox_list(
input: &[u8],
) -> IResult<&[u8], (Option<Vec<FlagNameAttribute>>, Option<char>, Mailbox)> {
let mut parser = tuple((
delimited(tag(b"("), opt(mbx_list_flags), tag(b")")),
SP,
alt((
map(delimited(DQUOTE, quoted_char, DQUOTE), Option::Some),
value(None, nil),
)),
SP,
mailbox,
));
let (remaining, (mbx_list_flags, _, maybe_delimiter, _, mailbox)) = parser(input)?;
Ok((remaining, (mbx_list_flags, maybe_delimiter, mailbox)))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_mailbox() {
assert!(mailbox(b"\"iNbOx\"").is_ok());
assert!(mailbox(b"{3}\r\naaa\r\n").is_ok());
assert!(mailbox(b"inbox ").is_ok());
assert!(mailbox(b"inbox.sent ").is_ok());
assert!(mailbox(b"aaa").is_err());
}
}