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 mut pairs = pair.into_inner();
let date = build_nstring(unwrap1(pairs.next().unwrap())); let date = build_nstring(unwrap1(pairs.next().unwrap()));
let subject = build_nstring(unwrap1(pairs.next().unwrap())); let subject = build_nstring(unwrap1(pairs.next().unwrap()));
pairs.next().unwrap(); // env_from
pairs.next().unwrap(); // env_sender let address1 = |r: Rule, pair: Pair<Rule>| -> Option<Vec<Address>> {
pairs.next().unwrap(); // env_reply_to assert!(matches!(pair.as_rule(), r));
pairs.next().unwrap(); // env_to let pair = unwrap1(pair);
pairs.next().unwrap(); // env_cc match pair.as_rule() {
pairs.next().unwrap(); // env_bcc 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 in_reply_to = build_nstring(unwrap1(pairs.next().unwrap()));
let message_id = build_nstring(unwrap1(pairs.next().unwrap())); let message_id = build_nstring(unwrap1(pairs.next().unwrap()));
Envelope { Envelope {
date, date,
subject, subject,
from: None, from,
sender: None, sender,
reply_to: None, reply_to,
to: None, to,
cc: None, cc,
bcc: None, bcc,
in_reply_to, in_reply_to,
message_id, message_id,
} }
@ -483,6 +494,8 @@ fn build_zone(pair: Pair<Rule>) -> FixedOffset {
} }
fn build_date_time(pair: Pair<Rule>) -> DateTime<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 mut pairs = pair.into_inner();
let pair = pairs.next().unwrap(); let pair = pairs.next().unwrap();
assert!(matches!(pair.as_rule(), Rule::date_day_fixed)); 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) 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_time = { dquote_ ~ date_day_fixed ~ "-" ~ date_month ~ "-" ~ date_year ~ sp ~ time ~ sp ~ zone ~ dquote_ }
date_year = @{ digit{4} } date_year = @{ digit{4} }
digit_nz = @{ '\x31'..'\x39' } digit_nz = @{ '\x31'..'\x39' }
env_bcc = { "(" ~ address{1,} ~ ")" | nil } env_address1 = { "(" ~ address{1,} ~ ")" }
env_cc = { "(" ~ address{1,} ~ ")" | nil } env_bcc = { env_address1 | nil }
env_cc = { env_address1 | nil }
env_date = { nstring } env_date = { nstring }
env_from = { "(" ~ address{1,} ~ ")" | nil } env_from = { env_address1 | nil }
env_in_reply_to = { nstring } env_in_reply_to = { nstring }
env_message_id = { nstring } env_message_id = { nstring }
env_reply_to = { "(" ~ address{1,} ~ ")" | nil } env_reply_to = { env_address1 | nil }
env_sender = { "(" ~ address{1,} ~ ")" | nil } env_sender = { env_address1 | nil }
env_subject = { nstring } 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 ~ ")" } 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 = { "\\Answered" | "\\Flagged" | "\\Deleted" | "\\Seen" | "\\Draft" | flag_keyword | flag_extension }
flag_extension = @{ "\\" ~ atom } flag_extension = @{ "\\" ~ atom }

View file

@ -22,6 +22,21 @@ fn test_literal() {
assert_eq!(p("{7}\r\nhellosu"), Ok("hellosu".to_owned())); 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] #[test]
fn test_zone() { fn test_zone() {
let p = parse(Rule::zone, build_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!( assert_eq!(
parse_response(concat!( 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))"#, 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 { AttributeValue::Envelope(Envelope {
date: Some("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)".to_owned()), date: Some("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)".to_owned()),
subject: Some("IMAP4rev1 WG mtg summary and minutes".to_owned()), subject: Some("IMAP4rev1 WG mtg summary and minutes".to_owned()),
from: None, from: Some(vec![terry_addr.clone()]),
sender: None, sender: Some(vec![terry_addr.clone()]),
reply_to: None, reply_to: Some(vec![terry_addr.clone()]),
to: None, to: Some(vec![imap_addr.clone()]),
cc: None, cc: Some(vec![minutes_addr.clone(), john_addr.clone()]),
bcc: None, bcc: None,
in_reply_to: None, in_reply_to: None,
message_id: Some("<B27397-0100000@cac.washington.edu>".to_owned()), message_id: Some("<B27397-0100000@cac.washington.edu>".to_owned()),

View file

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