mailbox_data
This commit is contained in:
parent
fed8031f5c
commit
1276ead25f
3 changed files with 140 additions and 36 deletions
imap/src
|
@ -1,4 +1,8 @@
|
|||
use pest::{error::Error, Parser, iterators::{Pair, Pairs}};
|
||||
use pest::{
|
||||
error::Error,
|
||||
iterators::{Pair, Pairs},
|
||||
Parser,
|
||||
};
|
||||
|
||||
use crate::response::*;
|
||||
|
||||
|
@ -6,8 +10,8 @@ use crate::response::*;
|
|||
#[grammar = "parser/rfc3501.pest"]
|
||||
struct Rfc3501;
|
||||
|
||||
pub fn parse_capability(s: &str) -> Result<Capability, Error<Rule>> {
|
||||
let mut pairs = Rfc3501::parse(Rule::capability, s)?;
|
||||
pub fn parse_capability(s: impl AsRef<str>) -> Result<Capability, Error<Rule>> {
|
||||
let mut pairs = Rfc3501::parse(Rule::capability, s.as_ref())?;
|
||||
let pair = pairs.next().unwrap();
|
||||
let cap = match pair.as_rule() {
|
||||
Rule::capability => {
|
||||
|
@ -27,60 +31,111 @@ pub fn parse_capability(s: &str) -> Result<Capability, Error<Rule>> {
|
|||
Ok(cap)
|
||||
}
|
||||
|
||||
pub fn parse_response(s: &str) -> Result<Response, Error<Rule>> {
|
||||
let mut pairs = Rfc3501::parse(Rule::response, s)?;
|
||||
pub fn parse_response(s: impl AsRef<str>) -> Result<Response, Error<Rule>> {
|
||||
let mut pairs = Rfc3501::parse(Rule::response, s.as_ref())?;
|
||||
let pair = pairs.next().unwrap();
|
||||
Ok(build_response(pair))
|
||||
}
|
||||
|
||||
fn build_response(pair: Pair<Rule>) -> Response {
|
||||
if !matches!(pair.as_rule(), Rule::response) {
|
||||
unreachable!("{:#?}", pair);
|
||||
}
|
||||
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
match pair.as_rule() {
|
||||
Rule::response => {
|
||||
Rule::response_done => {
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
match pair.as_rule() {
|
||||
Rule::response_data => {
|
||||
Rule::response_tagged => {
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
match pair.as_rule() {
|
||||
Rule::resp_cond_state => {
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
let status = build_status(pair);
|
||||
let mut code = None;
|
||||
let mut information = None;
|
||||
let tag = pair.as_str().to_owned();
|
||||
|
||||
for pair in pairs {
|
||||
if let resp_text = pair.as_rule() {
|
||||
information = Some(pair.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
Response::Data { status, code, information }
|
||||
}
|
||||
_ => unreachable!("{:?}", pair),
|
||||
let pair = pairs.next().unwrap();
|
||||
let (status, code, information) = build_resp_cond_state(pair);
|
||||
Response::Done {
|
||||
tag,
|
||||
status,
|
||||
code,
|
||||
information,
|
||||
}
|
||||
}
|
||||
_ => unreachable!("{:?}", pair),
|
||||
_ => unreachable!("{:#?}", pair),
|
||||
}
|
||||
}
|
||||
_ => unreachable!("{:?}", pair),
|
||||
Rule::response_data => {
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
match pair.as_rule() {
|
||||
Rule::resp_cond_state => {
|
||||
let (status, code, information) = build_resp_cond_state(pair);
|
||||
Response::Data {
|
||||
status,
|
||||
code,
|
||||
information,
|
||||
}
|
||||
}
|
||||
Rule::mailbox_data => Response::MailboxData(build_mailbox_data(pair)),
|
||||
_ => unreachable!("{:#?}", pair),
|
||||
}
|
||||
}
|
||||
_ => unreachable!("{:#?}", pair),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_resp_cond_state(pair: Pair<Rule>) -> (Status, Option<ResponseCode>, Option<String>) {
|
||||
if !matches!(pair.as_rule(), Rule::resp_cond_state) {
|
||||
unreachable!("{:#?}", pair);
|
||||
}
|
||||
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
let status = build_status(pair);
|
||||
let mut code = None;
|
||||
let mut information = None;
|
||||
|
||||
for pair in pairs {
|
||||
match pair.as_rule() {
|
||||
Rule::resp_text => information = Some(pair.as_str().to_owned()),
|
||||
_ => unreachable!("{:#?}", pair),
|
||||
}
|
||||
}
|
||||
(status, code, information)
|
||||
}
|
||||
|
||||
fn build_status(pair: Pair<Rule>) -> Status {
|
||||
match pair.as_rule() {
|
||||
Rule::resp_status => {
|
||||
match pair.as_str().to_uppercase().as_str() {
|
||||
"OK" => Status::Ok,
|
||||
"NO" => Status::No,
|
||||
"BAD" => Status::Bad,
|
||||
s => unreachable!("invalid status {:?}", s),
|
||||
}
|
||||
}
|
||||
Rule::resp_status => match pair.as_str().to_uppercase().as_str() {
|
||||
"OK" => Status::Ok,
|
||||
"NO" => Status::No,
|
||||
"BAD" => Status::Bad,
|
||||
s => unreachable!("invalid status {:?}", s),
|
||||
},
|
||||
_ => unreachable!("{:?}", pair),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_mailbox_data(pair: Pair<Rule>) -> MailboxData {
|
||||
if !matches!(pair.as_rule(), Rule::mailbox_data) {
|
||||
unreachable!("{:#?}", pair);
|
||||
}
|
||||
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
match pair.as_rule() {
|
||||
Rule::mailbox_data_exists => {
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
let number = pair.as_str().parse::<u32>().unwrap();
|
||||
MailboxData::Exists(number)
|
||||
}
|
||||
_ => unreachable!("{:#?}", pair),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[rustfmt::skip]
|
||||
mod tests {
|
||||
|
@ -115,5 +170,14 @@ mod tests {
|
|||
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("* 18 EXISTS\r\n"), Ok(Response::MailboxData(MailboxData::Exists(18))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ 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? ~ ^")") | (number ~ sp ~ ^"EXISTS") | (number ~ sp ~ ^"RECENT") }
|
||||
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_exists = { number ~ sp ~ ^"EXISTS" }
|
||||
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 }
|
||||
|
@ -116,7 +117,7 @@ zone = { ("+" | "-") ~ digit{4} }
|
|||
alpha = @{ '\x41'..'\x5a' | '\x61'..'\x7a' }
|
||||
char = @{ '\x01'..'\x7f' }
|
||||
cr = @{ "\x0d" }
|
||||
crlf = @{ cr ~ lf }
|
||||
crlf = _{ cr ~ lf }
|
||||
ctl = @{ '\x00'..'\x1f' | "\x7f" }
|
||||
digit = @{ '\x30'..'\x39' }
|
||||
dquote = @{ "\"" }
|
||||
|
|
|
@ -24,7 +24,7 @@ pub enum Response {
|
|||
uids: Vec<RangeInclusive<u32>>,
|
||||
},
|
||||
Fetch(u32, Vec<AttributeValue>),
|
||||
MailboxData(MailboxDatum),
|
||||
MailboxData(MailboxData),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
|
@ -63,7 +63,46 @@ pub enum UidSetMember {
|
|||
pub enum AttributeValue {}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MailboxDatum {}
|
||||
pub enum MailboxData {
|
||||
Exists(u32),
|
||||
Flags(Vec<String>),
|
||||
List {
|
||||
flags: Vec<String>,
|
||||
delimiter: Option<String>,
|
||||
name: String,
|
||||
},
|
||||
Search(Vec<u32>),
|
||||
Status {
|
||||
mailbox: String,
|
||||
status: Vec<StatusAttribute>,
|
||||
},
|
||||
Recent(u32),
|
||||
MetadataSolicited {
|
||||
mailbox: String,
|
||||
values: Vec<Metadata>,
|
||||
},
|
||||
MetadataUnsolicited {
|
||||
mailbox: String,
|
||||
values: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct Metadata {
|
||||
pub entry: String,
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum StatusAttribute {
|
||||
HighestModSeq(u64), // RFC 4551
|
||||
Messages(u32),
|
||||
Recent(u32),
|
||||
UidNext(u32),
|
||||
UidValidity(u32),
|
||||
Unseen(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
|
|
Loading…
Reference in a new issue