use abnf_core::streaming::{dquote, sp};
use imap_types::{
core::QuotedChar,
flag::FlagNameAttribute,
mailbox::{ListCharString, ListMailbox, Mailbox},
response::Data,
utils::indicators::is_list_char,
};
#[cfg(feature = "ext_condstore_qresync")]
use nom::character::streaming::char;
use nom::{
branch::alt,
bytes::streaming::{tag, tag_no_case, take_while1},
combinator::{map, opt, value},
multi::many0,
sequence::{delimited, preceded, terminated, tuple},
};
#[cfg(feature = "ext_condstore_qresync")]
use crate::extensions::condstore_qresync::search_sort_mod_seq;
#[cfg(feature = "ext_metadata")]
use crate::extensions::metadata::metadata_resp;
#[cfg(feature = "ext_namespace")]
use crate::extensions::namespace::namespace_response;
use crate::{
core::{astring, nil, number, nz_number, quoted_char, string},
decode::IMAPResult,
extensions::{
quota::{quota_response, quotaroot_response},
thread::thread_data,
},
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(preceded(tag_no_case(b"FLAGS "), flag_list), Data::Flags),
map(
preceded(tag_no_case(b"LIST "), mailbox_list),
|(items, delimiter, mailbox)| Data::List {
items: items.unwrap_or_default(),
mailbox,
delimiter,
},
),
map(
preceded(tag_no_case(b"LSUB "), mailbox_list),
|(items, delimiter, mailbox)| Data::Lsub {
items: items.unwrap_or_default(),
mailbox,
delimiter,
},
),
#[cfg(not(feature = "ext_condstore_qresync"))]
map(
#[cfg(not(feature = "quirk_trailing_space_search"))]
tuple((tag_no_case(b"SEARCH"), many0(preceded(sp, nz_number)))),
#[cfg(feature = "quirk_trailing_space_search")]
tuple((
tag_no_case(b"SEARCH"),
many0(preceded(sp, nz_number)),
opt(sp),
)),
#[cfg(not(feature = "quirk_trailing_space_search"))]
|(_, nums)| Data::Search(nums),
#[cfg(feature = "quirk_trailing_space_search")]
|(_, nums, _)| Data::Search(nums),
),
#[cfg(feature = "ext_condstore_qresync")]
map(
#[cfg(not(feature = "quirk_trailing_space_search"))]
tuple((
tag_no_case(b"SEARCH"),
many0(preceded(sp, nz_number)),
opt(preceded(char(' '), search_sort_mod_seq)),
)),
#[cfg(feature = "quirk_trailing_space_search")]
tuple((
tag_no_case(b"SEARCH"),
many0(preceded(sp, nz_number)),
opt(preceded(char(' '), search_sort_mod_seq)),
opt(sp),
)),
#[cfg(not(feature = "quirk_trailing_space_search"))]
|(_, nums, modseq)| Data::Search(nums, modseq),
#[cfg(feature = "quirk_trailing_space_search")]
|(_, nums, modseq, _)| Data::Search(nums, modseq),
),
#[cfg(not(feature = "ext_condstore_qresync"))]
map(
preceded(tag_no_case(b"SORT"), many0(preceded(sp, nz_number))),
Data::Sort,
),
#[cfg(feature = "ext_condstore_qresync")]
map(
tuple((
tag_no_case(b"SORT"),
many0(preceded(sp, nz_number)),
opt(preceded(char(' '), search_sort_mod_seq)),
)),
|(_, nums, modseq)| Data::Sort(nums, modseq),
),
thread_data,
map(
tuple((
tag_no_case(b"STATUS "),
mailbox,
delimited(tag(b" ("), opt(status_att_list), tag(b")")),
#[cfg(feature = "quirk_trailing_space_status")]
opt(sp),
#[cfg(not(feature = "quirk_trailing_space_status"))]
nom::combinator::success(()),
)),
|(_, mailbox, items, _)| Data::Status {
mailbox,
items: items.unwrap_or_default().into(),
},
),
#[cfg(feature = "ext_metadata")]
metadata_resp,
#[cfg(feature = "ext_namespace")]
namespace_response,
map(terminated(number, tag_no_case(b" EXISTS")), Data::Exists),
map(terminated(number, tag_no_case(b" RECENT")), Data::Recent),
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());
}
}