final section parser

This commit is contained in:
Quentin 2023-06-20 18:12:16 +02:00
parent 458d6517d1
commit 2b7b5664c1
Signed by: quentin
GPG key ID: E9602264D639FF68
7 changed files with 170 additions and 3 deletions

View file

@ -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 {

View file

@ -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
View 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
}
)
}
}

View file

@ -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")),

View 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!",
});
}
}

View file

@ -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;