diff --git a/imap/src/parser/mod.rs b/imap/src/parser/mod.rs index 16575ba..1a92245 100644 --- a/imap/src/parser/mod.rs +++ b/imap/src/parser/mod.rs @@ -118,6 +118,30 @@ fn build_status(pair: Pair) -> Status { } } +fn build_flag_list(pair: Pair) -> Vec { + if !matches!(pair.as_rule(), Rule::flag_list) { + unreachable!("{:#?}", pair); + } + + pair.into_inner().map(build_flag).collect() +} + +fn build_flag(pair: Pair) -> Flag { + if !matches!(pair.as_rule(), Rule::flag) { + unreachable!("{:#?}", pair); + } + + match pair.as_str() { + "\\Answered" => Flag::Answered, + "\\Flagged" => Flag::Flagged, + "\\Deleted" => Flag::Deleted, + "\\Seen" => Flag::Seen, + "\\Draft" => Flag::Draft, + s if s.starts_with("\\") => Flag::Ext(s.to_owned()), + _ => unreachable!("{:#?}", pair.as_str()), + } +} + fn build_mailbox_data(pair: Pair) -> MailboxData { if !matches!(pair.as_rule(), Rule::mailbox_data) { unreachable!("{:#?}", pair); @@ -132,18 +156,24 @@ fn build_mailbox_data(pair: Pair) -> MailboxData { let number = pair.as_str().parse::().unwrap(); MailboxData::Exists(number) } + Rule::mailbox_data_flags => { + let mut pairs = pair.into_inner(); + let pair = pairs.next().unwrap(); + let flags = build_flag_list(pair); + MailboxData::Flags(flags) + } _ => unreachable!("{:#?}", pair), } } #[cfg(test)] -#[rustfmt::skip] mod tests { use super::*; use crate::response::*; use pest::Parser; #[test] +#[rustfmt::skip] fn test_capability() { assert_eq!(parse_capability("IMAP4rev1"), Ok(Capability::Imap4rev1)); assert_eq!(parse_capability("LOGINDISABLED"), Ok(Capability::Atom("LOGINDISABLED".to_owned()))); @@ -155,6 +185,7 @@ mod tests { } #[test] +#[rustfmt::skip] fn test_nil() { assert!(Rfc3501::parse(Rule::nil, "NIL").is_ok()); assert!(Rfc3501::parse(Rule::nil, "anything else").is_err()); @@ -165,19 +196,39 @@ mod tests { // this little exchange is from section 8 of rfc3501 // https://tools.ietf.org/html/rfc3501#section-8 - assert_eq!(parse_response("* OK IMAP4rev1 Service Ready\r\n"), Ok(Response::Data { - status: Status::Ok, - code: None, - information: Some("IMAP4rev1 Service Ready".to_owned()), - })); + assert_eq!( + parse_response("* OK IMAP4rev1 Service Ready\r\n"), + Ok(Response::Data { + status: Status::Ok, + code: None, + information: Some("IMAP4rev1 Service Ready".to_owned()), + }) + ); - assert_eq!(parse_response("a001 OK LOGIN completed\r\n"), Ok(Response::Done { - tag: "a001".to_owned(), - status: Status::Ok, - code: None, - information: Some("LOGIN completed".to_owned()), - })); + assert_eq!( + parse_response("a001 OK LOGIN completed\r\n"), + Ok(Response::Done { + tag: "a001".to_owned(), + status: Status::Ok, + code: None, + information: Some("LOGIN completed".to_owned()), + }) + ); - assert_eq!(parse_response("* 18 EXISTS\r\n"), Ok(Response::MailboxData(MailboxData::Exists(18)))); + assert_eq!( + parse_response("* 18 EXISTS\r\n"), + Ok(Response::MailboxData(MailboxData::Exists(18))) + ); + + assert_eq!( + parse_response("* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n"), + Ok(Response::MailboxData(MailboxData::Flags(vec![ + Flag::Answered, + Flag::Flagged, + Flag::Deleted, + Flag::Seen, + Flag::Draft, + ]))) + ); } } diff --git a/imap/src/parser/rfc3501.pest b/imap/src/parser/rfc3501.pest index 69e38a3..44929fa 100644 --- a/imap/src/parser/rfc3501.pest +++ b/imap/src/parser/rfc3501.pest @@ -64,8 +64,9 @@ header_list = { "(" ~ header_fld_name ~ (sp ~ header_fld_name)* ~ ")" } list_wildcards = @{ "%" | "*" } literal = @{ "{" ~ number ~ "}" ~ crlf ~ char8* } mailbox = { ^"INBOX" | astring } -mailbox_data = { (^"FLAGS" ~ sp ~ flag_list) | (^"LIST" ~ sp ~ mailbox_list) | (^"LSUB" ~ sp ~ mailbox_list) | (^"SEARCH" ~ (sp ~ nz_number)*) | (^"STATUS" ~ sp ~ mailbox ~ sp ~ ^"(" ~ status_att_list? ~ ^")") | mailbox_data_exists | (number ~ sp ~ ^"RECENT") } +mailbox_data = { mailbox_data_flags | (^"LIST" ~ sp ~ mailbox_list) | (^"LSUB" ~ sp ~ mailbox_list) | (^"SEARCH" ~ (sp ~ nz_number)*) | (^"STATUS" ~ sp ~ mailbox ~ sp ~ ^"(" ~ status_att_list? ~ ^")") | mailbox_data_exists | (number ~ sp ~ ^"RECENT") } mailbox_data_exists = { number ~ sp ~ ^"EXISTS" } +mailbox_data_flags = { ^"FLAGS" ~ sp ~ flag_list } mailbox_list = { "(" ~ mbx_list_flags* ~ ")" ~ sp ~ (dquote ~ quoted_char ~ dquote | nil) ~ sp ~ mailbox } mbx_list_flags = { (mbx_list_oflag ~ sp)* ~ mbx_list_sflag ~ (sp ~ mbx_list_oflag)* | mbx_list_oflag ~ (sp ~ mbx_list_oflag)* } mbx_list_oflag = { "\\NoInferiors" | flag_extension } diff --git a/imap/src/response/mod.rs b/imap/src/response/mod.rs index 026f650..c7adfa4 100644 --- a/imap/src/response/mod.rs +++ b/imap/src/response/mod.rs @@ -65,7 +65,7 @@ pub enum AttributeValue {} #[derive(Clone, Debug, PartialEq, Eq)] pub enum MailboxData { Exists(u32), - Flags(Vec), + Flags(Vec), List { flags: Vec, delimiter: Option, @@ -87,6 +87,16 @@ pub enum MailboxData { }, } +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum Flag { + Answered, + Flagged, + Deleted, + Seen, + Draft, + Ext(String), +} + #[derive(Debug, Eq, PartialEq, Clone)] pub struct Metadata { pub entry: String,