final section parser
This commit is contained in:
parent
458d6517d1
commit
2b7b5664c1
7 changed files with 170 additions and 3 deletions
|
@ -15,10 +15,10 @@ use crate::fragments::whitespace::{fws, is_obs_no_ws_ctl};
|
||||||
use crate::fragments::words::{atom, is_vchar};
|
use crate::fragments::words::{atom, is_vchar};
|
||||||
use crate::error::IMFError;
|
use crate::error::IMFError;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Default)]
|
||||||
pub struct Unstructured(pub String);
|
pub struct Unstructured(pub String);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Default)]
|
||||||
pub struct PhraseList(pub Vec<String>);
|
pub struct PhraseList(pub Vec<String>);
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a lazy::Unstructured<'a>> for Unstructured {
|
impl<'a> TryFrom<&'a lazy::Unstructured<'a>> for Unstructured {
|
||||||
|
|
|
@ -15,6 +15,7 @@ mod trace;
|
||||||
mod datetime;
|
mod datetime;
|
||||||
pub mod lazy;
|
pub mod lazy;
|
||||||
pub mod eager;
|
pub mod eager;
|
||||||
|
pub mod section;
|
||||||
|
|
||||||
// Header blocks
|
// Header blocks
|
||||||
pub mod header;
|
pub mod header;
|
||||||
|
|
81
src/fragments/section.rs
Normal file
81
src/fragments/section.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use chrono::{DateTime, FixedOffset};
|
||||||
|
use crate::fragments::model::{
|
||||||
|
MailboxList, MailboxRef, AddressList,
|
||||||
|
MessageId, MessageIdList, AddressRef};
|
||||||
|
use crate::fragments::misc_token::{Unstructured, PhraseList};
|
||||||
|
use crate::fragments::trace::ReceivedLog;
|
||||||
|
use crate::fragments::eager::Field;
|
||||||
|
use crate::fragments::lazy;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Default)]
|
||||||
|
pub struct Section<'a> {
|
||||||
|
// 3.6.1. The Origination Date Field
|
||||||
|
pub date: Option<DateTime<FixedOffset>>,
|
||||||
|
|
||||||
|
// 3.6.2. Originator Fields
|
||||||
|
pub from: Vec<MailboxRef>,
|
||||||
|
pub sender: Option<MailboxRef>,
|
||||||
|
pub reply_to: Vec<AddressRef>,
|
||||||
|
|
||||||
|
// 3.6.3. Destination Address Fields
|
||||||
|
pub to: Vec<AddressRef>,
|
||||||
|
pub cc: Vec<AddressRef>,
|
||||||
|
pub bcc: Vec<AddressRef>,
|
||||||
|
|
||||||
|
// 3.6.4. Identification Fields
|
||||||
|
pub msg_id: Option<MessageId<'a>>,
|
||||||
|
pub in_reply_to: Vec<MessageId<'a>>,
|
||||||
|
pub references: Vec<MessageId<'a>>,
|
||||||
|
|
||||||
|
// 3.6.5. Informational Fields
|
||||||
|
pub subject: Option<Unstructured>,
|
||||||
|
pub comments: Vec<Unstructured>,
|
||||||
|
pub keywords: Vec<PhraseList>,
|
||||||
|
|
||||||
|
// 3.6.6 Not implemented
|
||||||
|
// 3.6.7 Trace Fields
|
||||||
|
pub return_path: Vec<MailboxRef>,
|
||||||
|
pub received: Vec<ReceivedLog<'a>>,
|
||||||
|
|
||||||
|
// 3.6.8. Optional Fields
|
||||||
|
pub optional: HashMap<&'a str, Unstructured>,
|
||||||
|
|
||||||
|
// Recovery
|
||||||
|
pub bad_fields: Vec<lazy::Field<'a>>,
|
||||||
|
pub unparsed: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//@FIXME min and max limits are not enforced,
|
||||||
|
// it may result in missing data or silently overriden data.
|
||||||
|
impl<'a> From<Vec<Field<'a>>> for Section<'a> {
|
||||||
|
fn from(field_list: Vec<Field<'a>>) -> Self {
|
||||||
|
field_list.into_iter().fold(
|
||||||
|
Section::default(),
|
||||||
|
|mut section, field| {
|
||||||
|
match field {
|
||||||
|
Field::Date(v) => section.date = Some(v),
|
||||||
|
Field::From(v) => section.from.extend(v),
|
||||||
|
Field::Sender(v) => section.sender = Some(v),
|
||||||
|
Field::ReplyTo(v) => section.reply_to.extend(v),
|
||||||
|
Field::To(v) => section.to.extend(v),
|
||||||
|
Field::Cc(v) => section.cc.extend(v),
|
||||||
|
Field::Bcc(v) => section.bcc.extend(v),
|
||||||
|
Field::MessageID(v) => section.msg_id = Some(v),
|
||||||
|
Field::InReplyTo(v) => section.in_reply_to.extend(v),
|
||||||
|
Field::References(v) => section.references.extend(v),
|
||||||
|
Field::Subject(v) => section.subject = Some(v),
|
||||||
|
Field::Comments(v) => section.comments.push(v),
|
||||||
|
Field::Keywords(v) => section.keywords.push(v),
|
||||||
|
Field::ReturnPath(v) => section.return_path.push(v),
|
||||||
|
Field::Received(v) => section.received.push(v),
|
||||||
|
Field::Optional(k, v) => { section.optional.insert(k, v); },
|
||||||
|
Field::Rescue(v) => section.unparsed.push(v),
|
||||||
|
};
|
||||||
|
section
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ mod tests {
|
||||||
use chrono::{FixedOffset, TimeZone};
|
use chrono::{FixedOffset, TimeZone};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_field_name() {
|
fn test_field_body() {
|
||||||
assert_eq!(Parsed::from(&field_lazy::Parsed {
|
assert_eq!(Parsed::from(&field_lazy::Parsed {
|
||||||
fields: vec![
|
fields: vec![
|
||||||
lazy::Field::From(lazy::MailboxList("hello@world.com,\r\n\talice@wonderlands.com\r\n")),
|
lazy::Field::From(lazy::MailboxList("hello@world.com,\r\n\talice@wonderlands.com\r\n")),
|
||||||
|
|
84
src/multipass/header_section.rs
Normal file
84
src/multipass/header_section.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use crate::fragments::section::Section;
|
||||||
|
use crate::multipass::field_eager;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Parsed<'a> {
|
||||||
|
pub fields: Section<'a>,
|
||||||
|
pub body: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<field_eager::Parsed<'a>> for Parsed<'a> {
|
||||||
|
fn from(p: field_eager::Parsed<'a>) -> Self {
|
||||||
|
Parsed {
|
||||||
|
fields: p.fields.into(),
|
||||||
|
body: p.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::fragments::eager;
|
||||||
|
use crate::fragments::model;
|
||||||
|
use chrono::{FixedOffset, TimeZone};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_section() {
|
||||||
|
assert_eq!(Parsed::from(field_eager::Parsed {
|
||||||
|
fields: vec![
|
||||||
|
eager::Field::From(vec![
|
||||||
|
model::MailboxRef {
|
||||||
|
name: None,
|
||||||
|
addrspec: model::AddrSpec {
|
||||||
|
local_part: "hello".into(),
|
||||||
|
domain: "world.com".into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
model::MailboxRef {
|
||||||
|
name: None,
|
||||||
|
addrspec: model::AddrSpec {
|
||||||
|
local_part: "alice".into(),
|
||||||
|
domain: "wonderlands.com".into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
eager::Field::Date(
|
||||||
|
FixedOffset::east_opt(0)
|
||||||
|
.unwrap()
|
||||||
|
.with_ymd_and_hms(1997, 03, 12, 7, 33, 25)
|
||||||
|
.unwrap()
|
||||||
|
),
|
||||||
|
],
|
||||||
|
body: b"Hello world!",
|
||||||
|
}),
|
||||||
|
Parsed {
|
||||||
|
fields: Section {
|
||||||
|
from: vec![
|
||||||
|
model::MailboxRef {
|
||||||
|
name: None,
|
||||||
|
addrspec: model::AddrSpec {
|
||||||
|
local_part: "hello".into(),
|
||||||
|
domain: "world.com".into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
model::MailboxRef {
|
||||||
|
name: None,
|
||||||
|
addrspec: model::AddrSpec {
|
||||||
|
local_part: "alice".into(),
|
||||||
|
domain: "wonderlands.com".into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
date: Some(FixedOffset::east_opt(0)
|
||||||
|
.unwrap()
|
||||||
|
.with_ymd_and_hms(1997, 03, 12, 7, 33, 25)
|
||||||
|
.unwrap()),
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
body: b"Hello world!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,3 +3,4 @@ pub mod guess_charset;
|
||||||
pub mod extract_fields;
|
pub mod extract_fields;
|
||||||
pub mod field_lazy;
|
pub mod field_lazy;
|
||||||
pub mod field_eager;
|
pub mod field_eager;
|
||||||
|
pub mod header_section;
|
||||||
|
|
Loading…
Reference in a new issue