use abnf_core::streaming::{dquote, sp};
use imap_types::{
core::QuotedChar,
flag::FlagNameAttribute,
mailbox::{ListCharString, ListMailbox, Mailbox},
response::Data,
utils::indicators::is_list_char,
};
use nom::{
branch::alt,
bytes::streaming::{tag, tag_no_case, take_while1},
combinator::{map, opt, value},
multi::many0,
sequence::{delimited, preceded, tuple},
};
use crate::{
core::{astring, nil, number, nz_number, quoted_char, string},
decode::IMAPResult,
extensions::quota::{quota_response, quotaroot_response},
flag::{flag_list, mbx_list_flags},
status::status_att_list,
};
pub(crate) fn list_mailbox(input: &[u8]) -> IMAPResult<&[u8], ListMailbox> {
alt((
map(take_while1(is_list_char), |bytes: &[u8]| {
ListMailbox::Token(ListCharString::unvalidated(
std::str::from_utf8(bytes).unwrap(),
))
}),
map(string, ListMailbox::String),
))(input)
}
pub(crate) fn mailbox(input: &[u8]) -> IMAPResult<&[u8], Mailbox> {
map(astring, Mailbox::from)(input)
}
pub(crate) fn mailbox_data(input: &[u8]) -> IMAPResult<&[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, _, items)| Data::Status {
mailbox,
items: items.unwrap_or_default().into(),
},
),
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),
),
quotaroot_response,
quota_response,
))(input)
}
#[allow(clippy::type_complexity)]
pub(crate) fn mailbox_list(
input: &[u8],
) -> IMAPResult<&[u8], (Option<Vec<FlagNameAttribute>>, Option<QuotedChar>, 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 tests {
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());
}
}