destination address fields
This commit is contained in:
parent
6ae9a6a9aa
commit
de6926bb92
4 changed files with 105 additions and 14 deletions
|
@ -29,11 +29,11 @@ pub fn address(input: &str) -> IResult<&str, AddressRef> {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn group(input: &str) -> IResult<&str, GroupRef> {
|
pub fn group(input: &str) -> IResult<&str, GroupRef> {
|
||||||
let (input, (grp_name, _, grp_list, _, _)) =
|
let (input, (grp_name, _, grp_list, _, _)) =
|
||||||
tuple((phrase, tag(":"), group_list, tag(";"), opt(cfws)))(input)?;
|
tuple((phrase, tag(":"), opt(group_list), tag(";"), opt(cfws)))(input)?;
|
||||||
|
|
||||||
Ok((input, GroupRef {
|
Ok((input, GroupRef {
|
||||||
name: grp_name,
|
name: grp_name,
|
||||||
participants: grp_list,
|
participants: grp_list.unwrap_or(vec![]),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ pub fn group(input: &str) -> IResult<&str, GroupRef> {
|
||||||
/// group-list = mailbox-list / CFWS / obs-group-list
|
/// group-list = mailbox-list / CFWS / obs-group-list
|
||||||
/// ```
|
/// ```
|
||||||
pub fn group_list(input: &str) -> IResult<&str, Vec<MailboxRef>> {
|
pub fn group_list(input: &str) -> IResult<&str, Vec<MailboxRef>> {
|
||||||
alt((mailbox_list, mx_cfws))(input)
|
alt((mailbox_list, mailbox_cfws))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mx_cfws(input: &str) -> IResult<&str, Vec<MailboxRef>> {
|
fn mailbox_cfws(input: &str) -> IResult<&str, Vec<MailboxRef>> {
|
||||||
let (input, _) = cfws(input)?;
|
let (input, _) = cfws(input)?;
|
||||||
Ok((input, vec![]))
|
Ok((input, vec![]))
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,11 @@ pub fn address_list(input: &str) -> IResult<&str, Vec<AddressRef>> {
|
||||||
separated_list1(tag(","), address)(input)
|
separated_list1(tag(","), address)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn address_list_cfws(input: &str) -> IResult<&str, Vec<AddressRef>> {
|
||||||
|
let (input, _) = cfws(input)?;
|
||||||
|
Ok((input, vec![]))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use nom::{
|
use nom::{
|
||||||
IResult,
|
IResult,
|
||||||
|
branch::alt,
|
||||||
bytes::complete::take_while1,
|
bytes::complete::take_while1,
|
||||||
bytes::complete::tag,
|
bytes::complete::tag,
|
||||||
character::complete::space0,
|
character::complete::space0,
|
||||||
|
@ -15,7 +16,7 @@ use crate::words::vchar_seq;
|
||||||
use crate::misc_token::unstructured;
|
use crate::misc_token::unstructured;
|
||||||
use crate::model::{PermissiveHeaderSection, HeaderDate, MailboxRef, AddressRef};
|
use crate::model::{PermissiveHeaderSection, HeaderDate, MailboxRef, AddressRef};
|
||||||
use crate::mailbox::mailbox;
|
use crate::mailbox::mailbox;
|
||||||
use crate::address::{mailbox_list, address_list};
|
use crate::address::{mailbox_list, address_list, address_list_cfws};
|
||||||
|
|
||||||
/// HEADERS
|
/// HEADERS
|
||||||
|
|
||||||
|
@ -28,9 +29,11 @@ pub fn header_section(input: &str) -> IResult<&str, PermissiveHeaderSection> {
|
||||||
PermissiveHeaderSection::default,
|
PermissiveHeaderSection::default,
|
||||||
|mut section, head| {
|
|mut section, head| {
|
||||||
match head {
|
match head {
|
||||||
|
//@FIXME min and max limits are not enforced,
|
||||||
|
// it may result in missing data or silently overriden data.
|
||||||
|
|
||||||
// 3.6.1. The Origination Date Field
|
// 3.6.1. The Origination Date Field
|
||||||
HeaderField::Date(d) => {
|
HeaderField::Date(d) => {
|
||||||
//@FIXME only one date is allowed, what are we doing if multiple dates are
|
|
||||||
//encountered? Currently, we override...
|
//encountered? Currently, we override...
|
||||||
// | orig-date | 1 | 1 | |
|
// | orig-date | 1 | 1 | |
|
||||||
section.date = d;
|
section.date = d;
|
||||||
|
@ -38,7 +41,6 @@ pub fn header_section(input: &str) -> IResult<&str, PermissiveHeaderSection> {
|
||||||
|
|
||||||
// 3.6.2. Originator Fields
|
// 3.6.2. Originator Fields
|
||||||
HeaderField::From(v) => {
|
HeaderField::From(v) => {
|
||||||
//@FIXME override the from field if declared multiple times.
|
|
||||||
// | from | 1 | 1 | See sender and 3.6.2 |
|
// | from | 1 | 1 | See sender and 3.6.2 |
|
||||||
section.from = v;
|
section.from = v;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +54,18 @@ pub fn header_section(input: &str) -> IResult<&str, PermissiveHeaderSection> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.6.3. Destination Address Fields
|
// 3.6.3. Destination Address Fields
|
||||||
|
HeaderField::To(addr_list) => {
|
||||||
|
// | to | 0 | 1 | |
|
||||||
|
section.to = addr_list;
|
||||||
|
}
|
||||||
|
HeaderField::Cc(addr_list) => {
|
||||||
|
// | cc | 0 | 1 | |
|
||||||
|
section.cc = addr_list;
|
||||||
|
}
|
||||||
|
HeaderField::Bcc(addr_list) => {
|
||||||
|
// | bcc | 0 | 1 | |
|
||||||
|
section.bcc = addr_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
HeaderField::Subject(title) => {
|
HeaderField::Subject(title) => {
|
||||||
|
@ -81,9 +95,9 @@ enum HeaderField<'a> {
|
||||||
ReplyTo(Vec<AddressRef>),
|
ReplyTo(Vec<AddressRef>),
|
||||||
|
|
||||||
// 3.6.3. Destination Address Fields
|
// 3.6.3. Destination Address Fields
|
||||||
To,
|
To(Vec<AddressRef>),
|
||||||
Cc,
|
Cc(Vec<AddressRef>),
|
||||||
Bcc,
|
Bcc(Vec<AddressRef>),
|
||||||
|
|
||||||
// 3.6.4. Identification Fields
|
// 3.6.4. Identification Fields
|
||||||
MessageID,
|
MessageID,
|
||||||
|
@ -129,9 +143,11 @@ fn header_field(input: &str) -> IResult<&str, HeaderField> {
|
||||||
let (input, _) = tuple((tag(":"), space0))(input)?;
|
let (input, _) = tuple((tag(":"), space0))(input)?;
|
||||||
|
|
||||||
// Extract field body
|
// Extract field body
|
||||||
let (input, hfield) = match field_name {
|
let (input, hfield) = match field_name {
|
||||||
|
// 3.6.1. The Origination Date Field
|
||||||
"Date" => datetime(input)?,
|
"Date" => datetime(input)?,
|
||||||
|
|
||||||
|
// 3.6.2. Originator Fields
|
||||||
"From" => {
|
"From" => {
|
||||||
let (input, body) = mailbox_list(input)?;
|
let (input, body) = mailbox_list(input)?;
|
||||||
(input, HeaderField::From(body))
|
(input, HeaderField::From(body))
|
||||||
|
@ -145,6 +161,20 @@ fn header_field(input: &str) -> IResult<&str, HeaderField> {
|
||||||
(input, HeaderField::ReplyTo(body))
|
(input, HeaderField::ReplyTo(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.6.3. Destination Address Fields
|
||||||
|
"To" => {
|
||||||
|
let (input, body) = address_list(input)?;
|
||||||
|
(input, HeaderField::To(body))
|
||||||
|
},
|
||||||
|
"Cc" => {
|
||||||
|
let (input, body) = address_list(input)?;
|
||||||
|
(input, HeaderField::Cc(body))
|
||||||
|
},
|
||||||
|
"Bcc" => {
|
||||||
|
let (input, body) = opt(alt((address_list, address_list_cfws)))(input)?;
|
||||||
|
(input, HeaderField::Bcc(body.unwrap_or(vec![])))
|
||||||
|
},
|
||||||
|
|
||||||
"Subject" => {
|
"Subject" => {
|
||||||
let (input, body) = unstructured(input)?;
|
let (input, body) = unstructured(input)?;
|
||||||
(input, HeaderField::Subject(body))
|
(input, HeaderField::Subject(body))
|
||||||
|
@ -174,7 +204,7 @@ fn datetime(input: &str) -> IResult<&str, HeaderField> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::model::AddrSpec;
|
use crate::model::{GroupRef, AddrSpec};
|
||||||
|
|
||||||
// 3.6.1. The Origination Date Field
|
// 3.6.1. The Origination Date Field
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -229,6 +259,51 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3.6.3. Destination Address Fields
|
||||||
|
#[test]
|
||||||
|
fn test_to() {
|
||||||
|
assert_eq!(
|
||||||
|
header_field("To: A Group:Ed Jones <c@a.test>,joe@where.test,John <jdoe@one.test>;\r\n"),
|
||||||
|
Ok(("", HeaderField::To(vec![AddressRef::Many(GroupRef {
|
||||||
|
name: "A Group".into(),
|
||||||
|
participants: vec![
|
||||||
|
MailboxRef {
|
||||||
|
name: Some("Ed Jones".into()),
|
||||||
|
addrspec: AddrSpec { local_part: "c".into(), domain: "a.test".into() },
|
||||||
|
},
|
||||||
|
MailboxRef {
|
||||||
|
name: None,
|
||||||
|
addrspec: AddrSpec { local_part: "joe".into(), domain: "where.test".into() },
|
||||||
|
},
|
||||||
|
MailboxRef {
|
||||||
|
name: Some("John".into()),
|
||||||
|
addrspec: AddrSpec { local_part: "jdoe".into(), domain: "one.test".into() },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})])))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_cc() {
|
||||||
|
assert_eq!(
|
||||||
|
header_field("Cc: Undisclosed recipients:;\r\n"),
|
||||||
|
Ok(("", HeaderField::Cc(vec![AddressRef::Many(GroupRef {
|
||||||
|
name: "Undisclosed recipients".into(),
|
||||||
|
participants: vec![],
|
||||||
|
})])))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_bcc() {
|
||||||
|
assert_eq!(
|
||||||
|
header_field("Bcc: (empty)\r\n"),
|
||||||
|
Ok(("", HeaderField::Bcc(vec![])))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
header_field("Bcc: \r\n"),
|
||||||
|
Ok(("", HeaderField::Bcc(vec![])))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/model.rs
13
src/model.rs
|
@ -65,11 +65,20 @@ impl From<GroupRef> for AddressRef {
|
||||||
/// still extract some data.
|
/// still extract some data.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PermissiveHeaderSection<'a> {
|
pub struct PermissiveHeaderSection<'a> {
|
||||||
pub subject: Option<String>,
|
// 3.6.1. The Origination Date Field
|
||||||
|
pub date: HeaderDate,
|
||||||
|
|
||||||
|
// 3.6.2. Originator Fields
|
||||||
pub from: Vec<MailboxRef>,
|
pub from: Vec<MailboxRef>,
|
||||||
pub sender: Option<MailboxRef>,
|
pub sender: Option<MailboxRef>,
|
||||||
pub reply_to: Vec<AddressRef>,
|
pub reply_to: Vec<AddressRef>,
|
||||||
pub date: HeaderDate,
|
|
||||||
|
// 3.6.3. Destination Address Fields
|
||||||
|
pub to: Vec<AddressRef>,
|
||||||
|
pub cc: Vec<AddressRef>,
|
||||||
|
pub bcc: Vec<AddressRef>,
|
||||||
|
|
||||||
|
pub subject: Option<String>,
|
||||||
pub optional: HashMap<&'a str, String>,
|
pub optional: HashMap<&'a str, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ From: Mary Smith
|
||||||
Sender: imf@example.com
|
Sender: imf@example.com
|
||||||
Reply-To: "Mary Smith: Personal Account" <smith@home.example>
|
Reply-To: "Mary Smith: Personal Account" <smith@home.example>
|
||||||
To: John Doe <jdoe@machine.example>
|
To: John Doe <jdoe@machine.example>
|
||||||
|
Cc: imf2@example.com
|
||||||
|
Bcc: (hidden)
|
||||||
Subject: Re: Saying Hello
|
Subject: Re: Saying Hello
|
||||||
Message-ID: <3456@example.net>
|
Message-ID: <3456@example.net>
|
||||||
In-Reply-To: <1234@local.machine.example>
|
In-Reply-To: <1234@local.machine.example>
|
||||||
|
|
Loading…
Reference in a new issue