diff --git a/imap/src/parser/mod.rs b/imap/src/parser/mod.rs index 88f1d52..2c02b9a 100644 --- a/imap/src/parser/mod.rs +++ b/imap/src/parser/mod.rs @@ -1,4 +1,4 @@ -use pest::{error::Error, Parser}; +use pest::{error::Error, Parser, iterators::{Pair, Pairs}}; use crate::response::*; @@ -11,8 +11,8 @@ pub fn parse_capability(s: &str) -> Result> { let pair = pairs.next().unwrap(); let cap = match pair.as_rule() { Rule::capability => { - let mut inner = pair.into_inner(); - let pair = inner.next().unwrap(); + let mut pairs = pair.into_inner(); + let pair = pairs.next().unwrap(); match pair.as_rule() { Rule::auth_type => Capability::Auth(pair.as_str().to_uppercase().to_owned()), Rule::atom => match pair.as_str() { @@ -28,7 +28,57 @@ pub fn parse_capability(s: &str) -> Result> { } pub fn parse_response(s: &str) -> Result> { - todo!() + let mut pairs = Rfc3501::parse(Rule::response, s)?; + let pair = pairs.next().unwrap(); + Ok(build_response(pair)) +} + +fn build_response(pair: Pair) -> Response { + match pair.as_rule() { + Rule::response => { + let mut pairs = pair.into_inner(); + let pair = pairs.next().unwrap(); + match pair.as_rule() { + Rule::response_data => { + 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; + + 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), + } + } + _ => unreachable!("{:?}", pair), + } + } + _ => unreachable!("{:?}", pair), + } +} + +fn build_status(pair: Pair) -> 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), + } + } + _ => unreachable!("{:?}", pair), + } } #[cfg(test)] @@ -54,4 +104,16 @@ mod tests { assert!(Rfc3501::parse(Rule::nil, "NIL").is_ok()); assert!(Rfc3501::parse(Rule::nil, "anything else").is_err()); } + + #[test] + fn test_section_8() { + // 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()), + })); + } } diff --git a/imap/src/parser/rfc3501.pest b/imap/src/parser/rfc3501.pest index efde2c1..b9b6270 100644 --- a/imap/src/parser/rfc3501.pest +++ b/imap/src/parser/rfc3501.pest @@ -85,7 +85,8 @@ quoted = @{ dquote ~ quoted_char* ~ dquote } quoted_char = @{ (!quoted_specials ~ char) | ("\\" ~ quoted_specials) } quoted_specials = @{ dquote | "\\" } resp_cond_bye = { ^"BYE" ~ sp ~ resp_text } -resp_cond_state = { (^"OK" | ^"NO" | ^"BAD") ~ resp_text } +resp_cond_state = { resp_status ~ sp ~ resp_text } +resp_status = { (^"OK" | ^"NO" | ^"BAD") } resp_specials = @{ "]" } resp_text = { ("[" ~ resp_text_code ~ "]" ~ sp)? ~ text } resp_text_code = { ^"ALERT" | (^"BADCHARSET" ~ (sp ~ "(" ~ astring ~ (sp ~ astring)* ~ ")")?) | capability_data | ^"PARSE" | (^"PERMANENTFLAGS" ~ sp ~ "(" ~ (flag_perm ~ (sp ~ flag_perm)*)? ~ ")") | ^"READ-ONLY" | ^"READ-WRITE" | ^"TRYCREATE" | (^"UIDNEXT" ~ sp ~ nz_number) | (^"UIDVALIDITY" ~ sp ~ nz_number) | (^"UNSEEN" ~ sp ~ nz_number) | (atom ~ (sp ~ resp_text_code_atom)?) } @@ -120,5 +121,5 @@ ctl = @{ '\x00'..'\x1f' | "\x7f" } digit = @{ '\x30'..'\x39' } dquote = @{ "\"" } lf = @{ "\x0a" } -sp = @{ " " } +sp = _{ " " } diff --git a/imap/src/response/mod.rs b/imap/src/response/mod.rs index df8af08..b8be675 100644 --- a/imap/src/response/mod.rs +++ b/imap/src/response/mod.rs @@ -1,6 +1,6 @@ use std::ops::RangeInclusive; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Response { Capabilities(Vec), Continue { @@ -34,7 +34,7 @@ pub enum Capability { Atom(String), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ResponseCode { Alert, BadCharset(Option>), @@ -53,16 +53,16 @@ pub enum ResponseCode { UidNotSticky, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum UidSetMember { UidRange(RangeInclusive), Uid(u32), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum AttributeValue {} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum MailboxDatum {} #[derive(Clone, Debug, Eq, PartialEq)]