1use std::borrow::Cow;
8
9use nom::{
10 branch::alt,
11 bytes::streaming::{tag, tag_no_case},
12 character::streaming::space1,
13 combinator::map,
14 multi::many0,
15 multi::separated_list0,
16 sequence::{delimited, preceded, tuple},
17 IResult,
18};
19
20use crate::parser::core::astring_utf8;
21use crate::types::*;
22
23use super::core::number_64;
24
25pub(crate) fn quota(i: &[u8]) -> IResult<&[u8], Response<'_>> {
30 let (rest, (_, _, root_name, _, resources)) = tuple((
31 tag_no_case("QUOTA"),
32 space1,
33 map(astring_utf8, Cow::Borrowed),
34 space1,
35 quota_list,
36 ))(i)?;
37
38 Ok((
39 rest,
40 Response::Quota(Quota {
41 root_name,
42 resources,
43 }),
44 ))
45}
46
47pub(crate) fn quota_list(i: &[u8]) -> IResult<&[u8], Vec<QuotaResource<'_>>> {
51 delimited(tag("("), separated_list0(space1, quota_resource), tag(")"))(i)
52}
53
54pub(crate) fn quota_resource(i: &[u8]) -> IResult<&[u8], QuotaResource<'_>> {
58 let (rest, (name, _, usage, _, limit)) =
59 tuple((quota_resource_name, space1, number_64, space1, number_64))(i)?;
60
61 Ok((rest, QuotaResource { name, usage, limit }))
62}
63
64pub(crate) fn quota_resource_name(i: &[u8]) -> IResult<&[u8], QuotaResourceName<'_>> {
65 alt((
66 map(tag_no_case("STORAGE"), |_| QuotaResourceName::Storage),
67 map(tag_no_case("MESSAGE"), |_| QuotaResourceName::Message),
68 map(map(astring_utf8, Cow::Borrowed), QuotaResourceName::Atom),
69 ))(i)
70}
71
72pub(crate) fn quota_root(i: &[u8]) -> IResult<&[u8], Response<'_>> {
77 let (rest, (_, _, mailbox_name, quota_root_names)) = tuple((
78 tag_no_case("QUOTAROOT"),
79 space1,
80 map(astring_utf8, Cow::Borrowed),
81 many0(preceded(space1, map(astring_utf8, Cow::Borrowed))),
82 ))(i)?;
83
84 Ok((
85 rest,
86 Response::QuotaRoot(QuotaRoot {
87 mailbox_name,
88 quota_root_names,
89 }),
90 ))
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use assert_matches::assert_matches;
97 use std::borrow::Cow;
98
99 #[test]
100 fn test_quota() {
101 assert_matches!(
102 quota(b"QUOTA \"\" (STORAGE 10 512)"),
103 Ok((_, r)) => {
104 assert_eq!(
105 r,
106 Response::Quota(Quota {
107 root_name: Cow::Borrowed(""),
108 resources: vec![QuotaResource {
109 name: QuotaResourceName::Storage,
110 usage: 10,
111 limit: 512
112 }]
113 })
114 );
115 }
116 );
117 }
118
119 #[test]
120 fn test_quota_spaces() {
121 assert_matches!(
124 quota(b"QUOTA \"\" (STORAGE 0 2147483647 MESSAGE 0 2147483647)"),
125 Ok((_, r)) => {
126 assert_eq!(
127 r,
128 Response::Quota(Quota {
129 root_name: Cow::Borrowed(""),
130 resources: vec![QuotaResource {
131 name: QuotaResourceName::Storage,
132 usage: 0,
133 limit: 2147483647
134 }, QuotaResource {
135 name: QuotaResourceName::Message,
136 usage: 0,
137 limit: 2147483647
138 }]
139 })
140 );
141 }
142 );
143 }
144
145 #[test]
146 fn test_quota_response_data() {
147 assert_matches!(
148 crate::parser::rfc3501::response_data(b"* QUOTA \"\" (STORAGE 10 512)\r\n"),
149 Ok((_, r)) => {
150 assert_eq!(
151 r,
152 Response::Quota(Quota {
153 root_name: Cow::Borrowed(""),
154 resources: vec![QuotaResource {
155 name: QuotaResourceName::Storage,
156 usage: 10,
157 limit: 512
158 }]
159 })
160 );
161 }
162 );
163 }
164
165 #[test]
166 fn test_quota_list() {
167 assert_matches!(
168 quota_list(b"(STORAGE 10 512)"),
169 Ok((_, r)) => {
170 assert_eq!(
171 r,
172 vec![QuotaResource {
173 name: QuotaResourceName::Storage,
174 usage: 10,
175 limit: 512
176 }]
177 );
178 }
179 );
180
181 assert_matches!(
182 quota_list(b"(MESSAGE 100 512)"),
183 Ok((_, r)) => {
184 assert_eq!(
185 r,
186 vec![QuotaResource {
187 name: QuotaResourceName::Message,
188 usage: 100,
189 limit: 512
190 }]
191 );
192 }
193 );
194
195 assert_matches!(
196 quota_list(b"(DAILY 55 200)"),
197 Ok((_, r)) => {
198 assert_eq!(
199 r,
200 vec![QuotaResource {
201 name: QuotaResourceName::Atom(Cow::Borrowed("DAILY")),
202 usage: 55,
203 limit: 200
204 }]
205 );
206 }
207 );
208 }
209
210 #[test]
211 fn test_quota_root_response_data() {
212 assert_matches!(
213 crate::parser::rfc3501::response_data("* QUOTAROOT INBOX \"\"\r\n".as_bytes()),
214 Ok((_, r)) => {
215 assert_eq!(
216 r,
217 Response::QuotaRoot(QuotaRoot{
218 mailbox_name: Cow::Borrowed("INBOX"),
219 quota_root_names: vec![Cow::Borrowed("")]
220 })
221 );
222 }
223 );
224 }
225
226 fn terminated_quota_root(i: &[u8]) -> IResult<&[u8], Response<'_>> {
227 nom::sequence::terminated(quota_root, nom::bytes::streaming::tag("\r\n"))(i)
228 }
229
230 #[test]
231 fn test_quota_root_without_root_names() {
232 assert_matches!(
233 terminated_quota_root(b"QUOTAROOT comp.mail.mime\r\n"),
234 Ok((_, r)) => {
235 assert_eq!(
236 r,
237 Response::QuotaRoot(QuotaRoot{
238 mailbox_name: Cow::Borrowed("comp.mail.mime"),
239 quota_root_names: vec![]
240 })
241 );
242 }
243 );
244 }
245
246 #[test]
247 fn test_quota_root2() {
248 assert_matches!(
249 terminated_quota_root(b"QUOTAROOT INBOX HU\r\n"),
250 Ok((_, r)) => {
251 assert_eq!(
252 r,
253 Response::QuotaRoot(QuotaRoot{
254 mailbox_name: Cow::Borrowed("INBOX"),
255 quota_root_names: vec![Cow::Borrowed("HU")]
256 })
257 );
258 }
259 );
260
261 assert_matches!(
262 terminated_quota_root(b"QUOTAROOT INBOX \"\"\r\n"),
263 Ok((_, r)) => {
264 assert_eq!(
265 r,
266 Response::QuotaRoot(QuotaRoot{
267 mailbox_name: Cow::Borrowed("INBOX"),
268 quota_root_names: vec![Cow::Borrowed("")]
269 })
270 );
271 }
272 );
273
274 assert_matches!(
275 terminated_quota_root(b"QUOTAROOT \"Inbox\" \"#Account\"\r\n"),
276 Ok((_, r)) => {
277 assert_eq!(
278 r,
279 Response::QuotaRoot(QuotaRoot{
280 mailbox_name: Cow::Borrowed("Inbox"),
281 quota_root_names: vec![Cow::Borrowed("#Account")]
282 })
283 );
284 }
285 );
286
287 assert_matches!(
288 terminated_quota_root(b"QUOTAROOT \"Inbox\" \"#Account\" \"#Mailbox\"\r\n"),
289 Ok((_, r)) => {
290 assert_eq!(
291 r,
292 Response::QuotaRoot(QuotaRoot{
293 mailbox_name: Cow::Borrowed("Inbox"),
294 quota_root_names: vec![Cow::Borrowed("#Account"), Cow::Borrowed("#Mailbox")]
295 })
296 );
297 }
298 );
299 }
300}