add address parsing

This commit is contained in:
Michael Zhang 2021-03-09 05:55:31 -06:00
parent 424706d9a0
commit daa3b8cd61
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
4 changed files with 108 additions and 26 deletions

View file

@ -174,24 +174,35 @@ fn build_envelope(pair: Pair<Rule>) -> Envelope {
let mut pairs = pair.into_inner();
let date = build_nstring(unwrap1(pairs.next().unwrap()));
let subject = build_nstring(unwrap1(pairs.next().unwrap()));
pairs.next().unwrap(); // env_from
pairs.next().unwrap(); // env_sender
pairs.next().unwrap(); // env_reply_to
pairs.next().unwrap(); // env_to
pairs.next().unwrap(); // env_cc
pairs.next().unwrap(); // env_bcc
let address1 = |r: Rule, pair: Pair<Rule>| -> Option<Vec<Address>> {
assert!(matches!(pair.as_rule(), r));
let pair = unwrap1(pair);
match pair.as_rule() {
Rule::nil => None,
Rule::env_address1 => Some(pair.into_inner().map(build_address).collect()),
_ => unreachable!("{:?}", pair),
}
};
let from = address1(Rule::env_from, pairs.next().unwrap());
let sender = address1(Rule::env_sender, pairs.next().unwrap());
let reply_to = address1(Rule::env_reply_to, pairs.next().unwrap());
let to = address1(Rule::env_to, pairs.next().unwrap());
let cc = address1(Rule::env_cc, pairs.next().unwrap());
let bcc = address1(Rule::env_bcc, pairs.next().unwrap());
let in_reply_to = build_nstring(unwrap1(pairs.next().unwrap()));
let message_id = build_nstring(unwrap1(pairs.next().unwrap()));
Envelope {
date,
subject,
from: None,
sender: None,
reply_to: None,
to: None,
cc: None,
bcc: None,
from,
sender,
reply_to,
to,
cc,
bcc,
in_reply_to,
message_id,
}
@ -483,6 +494,8 @@ fn build_zone(pair: Pair<Rule>) -> FixedOffset {
}
fn build_date_time(pair: Pair<Rule>) -> DateTime<FixedOffset> {
assert!(matches!(pair.as_rule(), Rule::date_time));
let mut pairs = pair.into_inner();
let pair = pairs.next().unwrap();
assert!(matches!(pair.as_rule(), Rule::date_day_fixed));
@ -523,3 +536,31 @@ fn build_date_time(pair: Pair<Rule>) -> DateTime<FixedOffset> {
zone.ymd(year, month, day).and_hms(hour, minute, second)
}
fn build_address(pair: Pair<Rule>) -> Address {
assert!(matches!(pair.as_rule(), Rule::address));
let mut pairs = pair.into_inner();
let pair = pairs.next().unwrap();
assert!(matches!(pair.as_rule(), Rule::addr_name));
let name = build_nstring(unwrap1(pair));
let pair = pairs.next().unwrap();
assert!(matches!(pair.as_rule(), Rule::addr_adl));
let adl = build_nstring(unwrap1(pair));
let pair = pairs.next().unwrap();
assert!(matches!(pair.as_rule(), Rule::addr_mailbox));
let mailbox = build_nstring(unwrap1(pair));
let pair = pairs.next().unwrap();
assert!(matches!(pair.as_rule(), Rule::addr_host));
let host = build_nstring(unwrap1(pair));
Address {
name,
adl,
mailbox,
host,
}
}

View file

@ -44,16 +44,17 @@ date_month = { "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "
date_time = { dquote_ ~ date_day_fixed ~ "-" ~ date_month ~ "-" ~ date_year ~ sp ~ time ~ sp ~ zone ~ dquote_ }
date_year = @{ digit{4} }
digit_nz = @{ '\x31'..'\x39' }
env_bcc = { "(" ~ address{1,} ~ ")" | nil }
env_cc = { "(" ~ address{1,} ~ ")" | nil }
env_address1 = { "(" ~ address{1,} ~ ")" }
env_bcc = { env_address1 | nil }
env_cc = { env_address1 | nil }
env_date = { nstring }
env_from = { "(" ~ address{1,} ~ ")" | nil }
env_from = { env_address1 | nil }
env_in_reply_to = { nstring }
env_message_id = { nstring }
env_reply_to = { "(" ~ address{1,} ~ ")" | nil }
env_sender = { "(" ~ address{1,} ~ ")" | nil }
env_reply_to = { env_address1 | nil }
env_sender = { env_address1 | nil }
env_subject = { nstring }
env_to = { "(" ~ address{1,} ~ ")" | nil }
env_to = { env_address1 | nil }
envelope = { "(" ~ env_date ~ sp ~ env_subject ~ sp ~ env_from ~ sp ~ env_sender ~ sp ~ env_reply_to ~ sp ~ env_to ~ sp ~ env_cc ~ sp ~ env_bcc ~ sp ~ env_in_reply_to ~ sp ~ env_message_id ~ ")" }
flag = { "\\Answered" | "\\Flagged" | "\\Deleted" | "\\Seen" | "\\Draft" | flag_keyword | flag_extension }
flag_extension = @{ "\\" ~ atom }

View file

@ -22,6 +22,21 @@ fn test_literal() {
assert_eq!(p("{7}\r\nhellosu"), Ok("hellosu".to_owned()));
}
#[test]
fn test_address() -> Result<()> {
let p = parse(Rule::address, build_address);
assert_eq!(
p(r#"("Terry Gray" NIL "gray" "cac.washington.edu")"#)?,
Address {
name: Some("Terry Gray".to_owned()),
adl: None,
mailbox: Some("gray".to_owned()),
host: Some("cac.washington.edu".to_owned()),
}
);
Ok(())
}
#[test]
fn test_zone() {
let p = parse(Rule::zone, build_zone);
@ -133,6 +148,31 @@ fn test_section_8() {
}))
);
let terry_addr = Address {
name: Some("Terry Gray".to_owned()),
adl: None,
mailbox: Some("gray".to_owned()),
host: Some("cac.washington.edu".to_owned()),
};
let imap_addr = Address {
name: None,
adl: None,
mailbox: Some("imap".to_owned()),
host: Some("cac.washington.edu".to_owned()),
};
let minutes_addr = Address {
name: None,
adl: None,
mailbox: Some("minutes".to_owned()),
host: Some("CNRI.Reston.VA.US".to_owned()),
};
let john_addr = Address {
name: Some("John Klensin".to_owned()),
adl: None,
mailbox: Some("KLENSIN".to_owned()),
host: Some("MIT.EDU".to_owned()),
};
assert_eq!(
parse_response(concat!(
r#"* 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700" RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)" "IMAP4rev1 WG mtg summary and minutes" (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) ((NIL NIL "imap" "cac.washington.edu")) ((NIL NIL "minutes" "CNRI.Reston.VA.US")("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL "<B27397-0100000@cac.washington.edu>") BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 302892))"#,
@ -149,11 +189,11 @@ fn test_section_8() {
AttributeValue::Envelope(Envelope {
date: Some("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)".to_owned()),
subject: Some("IMAP4rev1 WG mtg summary and minutes".to_owned()),
from: None,
sender: None,
reply_to: None,
to: None,
cc: None,
from: Some(vec![terry_addr.clone()]),
sender: Some(vec![terry_addr.clone()]),
reply_to: Some(vec![terry_addr.clone()]),
to: Some(vec![imap_addr.clone()]),
cc: Some(vec![minutes_addr.clone(), john_addr.clone()]),
bcc: None,
in_reply_to: None,
message_id: Some("<B27397-0100000@cac.washington.edu>".to_owned()),

View file

@ -1,6 +1,6 @@
use std::collections::HashMap;
use chrono::{DateTime, Duration, Local, Datelike};
use chrono::{DateTime, Datelike, Duration, Local};
use chrono_humanize::HumanTime;
use panorama_imap::response::Envelope;
use tui::{
@ -35,7 +35,7 @@ fn humanize_timestamp(date: DateTime<Local>) -> String {
if diff < Duration::days(1) {
HumanTime::from(date).to_string()
}else if date.year() == now.year() {
} else if date.year() == now.year() {
date.format("%b %e %T").to_string()
} else {
date.to_rfc2822()
@ -73,7 +73,7 @@ impl MailTabState {
"".to_owned(),
id.to_string(),
meta.map(|m| humanize_timestamp(m.date)).unwrap_or_default(),
"".to_owned(),
meta.map(|m| m.from.clone()).unwrap_or_default(),
meta.map(|m| m.subject.clone()).unwrap_or_default(),
])
})