more work on response parser

This commit is contained in:
Michael Zhang 2021-02-22 16:56:23 -06:00
parent 875041edfd
commit fed8031f5c
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
3 changed files with 74 additions and 11 deletions

View file

@ -1,4 +1,4 @@
use pest::{error::Error, Parser}; use pest::{error::Error, Parser, iterators::{Pair, Pairs}};
use crate::response::*; use crate::response::*;
@ -11,8 +11,8 @@ pub fn parse_capability(s: &str) -> Result<Capability, Error<Rule>> {
let pair = pairs.next().unwrap(); let pair = pairs.next().unwrap();
let cap = match pair.as_rule() { let cap = match pair.as_rule() {
Rule::capability => { Rule::capability => {
let mut inner = pair.into_inner(); let mut pairs = pair.into_inner();
let pair = inner.next().unwrap(); let pair = pairs.next().unwrap();
match pair.as_rule() { match pair.as_rule() {
Rule::auth_type => Capability::Auth(pair.as_str().to_uppercase().to_owned()), Rule::auth_type => Capability::Auth(pair.as_str().to_uppercase().to_owned()),
Rule::atom => match pair.as_str() { Rule::atom => match pair.as_str() {
@ -28,7 +28,57 @@ pub fn parse_capability(s: &str) -> Result<Capability, Error<Rule>> {
} }
pub fn parse_response(s: &str) -> Result<Response, Error<Rule>> { pub fn parse_response(s: &str) -> Result<Response, Error<Rule>> {
todo!() let mut pairs = Rfc3501::parse(Rule::response, s)?;
let pair = pairs.next().unwrap();
Ok(build_response(pair))
}
fn build_response(pair: Pair<Rule>) -> 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<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),
}
}
_ => unreachable!("{:?}", pair),
}
} }
#[cfg(test)] #[cfg(test)]
@ -54,4 +104,16 @@ mod tests {
assert!(Rfc3501::parse(Rule::nil, "NIL").is_ok()); assert!(Rfc3501::parse(Rule::nil, "NIL").is_ok());
assert!(Rfc3501::parse(Rule::nil, "anything else").is_err()); 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()),
}));
}
} }

View file

@ -85,7 +85,8 @@ quoted = @{ dquote ~ quoted_char* ~ dquote }
quoted_char = @{ (!quoted_specials ~ char) | ("\\" ~ quoted_specials) } quoted_char = @{ (!quoted_specials ~ char) | ("\\" ~ quoted_specials) }
quoted_specials = @{ dquote | "\\" } quoted_specials = @{ dquote | "\\" }
resp_cond_bye = { ^"BYE" ~ sp ~ resp_text } 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_specials = @{ "]" }
resp_text = { ("[" ~ resp_text_code ~ "]" ~ sp)? ~ text } 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)?) } 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' } digit = @{ '\x30'..'\x39' }
dquote = @{ "\"" } dquote = @{ "\"" }
lf = @{ "\x0a" } lf = @{ "\x0a" }
sp = @{ " " } sp = _{ " " }

View file

@ -1,6 +1,6 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum Response { pub enum Response {
Capabilities(Vec<Capability>), Capabilities(Vec<Capability>),
Continue { Continue {
@ -34,7 +34,7 @@ pub enum Capability {
Atom(String), Atom(String),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum ResponseCode { pub enum ResponseCode {
Alert, Alert,
BadCharset(Option<Vec<String>>), BadCharset(Option<Vec<String>>),
@ -53,16 +53,16 @@ pub enum ResponseCode {
UidNotSticky, UidNotSticky,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum UidSetMember { pub enum UidSetMember {
UidRange(RangeInclusive<u32>), UidRange(RangeInclusive<u32>),
Uid(u32), Uid(u32),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum AttributeValue {} pub enum AttributeValue {}
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum MailboxDatum {} pub enum MailboxDatum {}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]