diff --git a/src/mime/type.rs b/src/mime/type.rs index 37d1cae..2ac51b6 100644 --- a/src/mime/type.rs +++ b/src/mime/type.rs @@ -43,41 +43,41 @@ pub fn parameter_list(input: &[u8]) -> IResult<&[u8], Vec> { #[derive(Debug, PartialEq)] pub enum Type { // Composite types - Multipart(MultipartDesc), - Message(MessageSubtype), + Multipart(Multipart), + Message(Message), // Discrete types - Text(TextDesc), + Text(Text), Binary, } impl Default for Type { fn default() -> Self { - Self::Text(TextDesc::default()) + Self::Text(Text::default()) } } impl<'a> From<&'a NaiveType<'a>> for Type { fn from(nt: &'a NaiveType<'a>) -> Self { match nt.main.to_ascii_lowercase().as_slice() { - b"multipart" => MultipartDesc::try_from(nt).map(Self::Multipart).unwrap_or(Self::default()), - b"message" => Self::Message(MessageSubtype::from(nt)), - b"text" => Self::Text(TextDesc::from(nt)), + b"multipart" => Multipart::try_from(nt).map(Self::Multipart).unwrap_or(Self::default()), + b"message" => Self::Message(Message::from(nt)), + b"text" => Self::Text(Text::from(nt)), _ => Self::Binary, } } } #[derive(Debug, PartialEq)] -pub struct MultipartDesc { +pub struct Multipart { pub subtype: MultipartSubtype, pub boundary: String, } -impl<'a> TryFrom<&'a NaiveType<'a>> for MultipartDesc { +impl<'a> TryFrom<&'a NaiveType<'a>> for Multipart { type Error = (); fn try_from(nt: &'a NaiveType<'a>) -> Result { nt.params.iter() .find(|x| x.name.to_ascii_lowercase().as_slice() == b"boundary") - .map(|boundary| MultipartDesc { + .map(|boundary| Multipart { subtype: MultipartSubtype::from(nt), boundary: boundary.value.to_string(), }) @@ -108,13 +108,13 @@ impl<'a> From<&NaiveType<'a>> for MultipartSubtype { } #[derive(Debug, PartialEq)] -pub enum MessageSubtype { +pub enum Message { RFC822, Partial, External, Unknown, } -impl<'a> From<&NaiveType<'a>> for MessageSubtype { +impl<'a> From<&NaiveType<'a>> for Message { fn from(nt: &NaiveType<'a>) -> Self { match nt.sub.to_ascii_lowercase().as_slice() { b"rfc822" => Self::RFC822, @@ -126,13 +126,13 @@ impl<'a> From<&NaiveType<'a>> for MessageSubtype { } #[derive(Debug, PartialEq, Default)] -pub struct TextDesc { +pub struct Text { pub subtype: TextSubtype, pub charset: EmailCharset, } -impl<'a> From<&NaiveType<'a>> for TextDesc { +impl<'a> From<&NaiveType<'a>> for Text { fn from(nt: &NaiveType<'a>) -> Self { - TextDesc { + Self { subtype: TextSubtype::from(nt), charset: nt.params.iter() .find(|x| x.name.to_ascii_lowercase().as_slice() == b"charset") @@ -189,7 +189,7 @@ mod tests { assert_eq!( nt.to_type(), - Type::Text(TextDesc { + Type::Text(Text { charset: EmailCharset::UTF_8, subtype: TextSubtype::Plain, }) @@ -203,7 +203,7 @@ mod tests { assert_eq!(rest, &[]); assert_eq!( nt.to_type(), - Type::Multipart(MultipartDesc { + Type::Multipart(Multipart { subtype: MultipartSubtype::Mixed, boundary: "--==_mimepart_64a3f2c69114f_2a13d020975fe".into(), }) @@ -217,7 +217,7 @@ mod tests { assert_eq!( nt.to_type(), - Type::Message(MessageSubtype::RFC822), + Type::Message(Message::RFC822), ); } diff --git a/src/old.multipass/body_structure.rs b/src/old.multipass/body_structure.rs deleted file mode 100644 index a05cee1..0000000 --- a/src/old.multipass/body_structure.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::fragments::part; -use crate::fragments::section::Section; -use crate::multipass::header_section; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub fields: Section<'a>, - pub body: part::PartNode<'a>, -} - -pub fn new<'a>(p: &'a header_section::Parsed<'a>) -> Parsed<'a> { - todo!(); - /*Parsed { - fields: p.fields, - body: p.body, - }*/ -} diff --git a/src/old.multipass/extract_fields.rs b/src/old.multipass/extract_fields.rs deleted file mode 100644 index e1a3d77..0000000 --- a/src/old.multipass/extract_fields.rs +++ /dev/null @@ -1,58 +0,0 @@ -use nom::{ - bytes::complete::is_not, - character::complete::space1, - combinator::{all_consuming, recognize}, - multi::{many0, many1}, - sequence::{pair, tuple}, - IResult, -}; - -use crate::error::IMFError; -use crate::fragments::fields; -use crate::multipass::field_lazy; -use crate::multipass::guess_charset; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub fields: Vec<&'a str>, - pub body: &'a [u8], -} - -pub fn new<'a>(gcha: &'a guess_charset::Parsed<'a>) -> Result, IMFError<'a>> { - fields(&gcha.header) - .map_err(|e| IMFError::ExtractFields(e)) - .map(|(_, fields)| Parsed { - fields, - body: gcha.body, - }) -} - -impl<'a> Parsed<'a> { - pub fn names(&'a self) -> field_lazy::Parsed<'a> { - field_lazy::new(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_extract() { - assert_eq!( - new(&guess_charset::Parsed { - header: "From: hello@world.com,\r\n\talice@wonderlands.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n".into(), - encoding: encoding_rs::UTF_8, - malformed: false, - body: b"Hello world!", - }), - Ok(Parsed { - fields: vec![ - "From: hello@world.com,\r\n\talice@wonderlands.com\r\n", - "Date: 12 Mar 1997 07:33:25 Z\r\n", - ], - body: b"Hello world!", - }) - ); - } -} diff --git a/src/old.multipass/field_eager.rs b/src/old.multipass/field_eager.rs deleted file mode 100644 index aec4b6e..0000000 --- a/src/old.multipass/field_eager.rs +++ /dev/null @@ -1,336 +0,0 @@ -use crate::fragments::eager; -use crate::multipass::field_lazy; -use crate::multipass::header_section; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub fields: Vec>, - pub body: &'a [u8], -} - -pub fn new<'a>(p: &'a field_lazy::Parsed<'a>) -> Parsed<'a> { - Parsed { - fields: p - .fields - .iter() - .filter_map(|entry| entry.try_into().ok()) - .collect(), - body: p.body, - } -} - -impl<'a> Parsed<'a> { - pub fn section(&'a self) -> header_section::Parsed<'a> { - header_section::new(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::fragments::lazy; - use crate::fragments::model; - use chrono::{FixedOffset, TimeZone}; - - #[test] - fn test_field_body() { - assert_eq!( - new(&field_lazy::Parsed { - fields: vec![ - lazy::Field::From(lazy::MailboxList( - "hello@world.com,\r\n\talice@wonderlands.com\r\n" - )), - lazy::Field::Date(lazy::DateTime("12 Mar 1997 07:33:25 Z\r\n")), - ], - body: b"Hello world!", - }), - 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!", - } - ); - } - - use crate::fragments::misc_token; - use crate::multipass::extract_fields; - fn lazy_eager(input: &str, func: F) - where - F: Fn(&eager::Field), - { - let field = extract_fields::Parsed { - fields: vec![input], - body: b"", - }; - let lazy = field_lazy::new(&field); - let eager = new(&lazy); - func(eager.fields.first().unwrap()) - } - - #[test] - fn test_from() { - lazy_eager( - "From: \"Joe Q. Public\" \r\n", - |from| { - assert_eq!( - from, - &eager::Field::From(vec![model::MailboxRef { - name: Some("Joe Q. Public".into()), - addrspec: model::AddrSpec { - local_part: "john.q.public".into(), - domain: "example.com".into(), - } - }]) - ) - }, - ); - } - - #[test] - fn test_sender() { - lazy_eager( - "Sender: Michael Jones \r\n", - |sender| { - assert_eq!( - sender, - &eager::Field::Sender(model::MailboxRef { - name: Some("Michael Jones".into()), - addrspec: model::AddrSpec { - local_part: "mjones".into(), - domain: "machine.example".into(), - }, - }) - ) - }, - ); - } - - #[test] - fn test_reply_to() { - lazy_eager( - "Reply-To: \"Mary Smith: Personal Account\" \r\n", - |reply_to| { - assert_eq!( - reply_to, - &eager::Field::ReplyTo(vec![model::AddressRef::Single(model::MailboxRef { - name: Some("Mary Smith: Personal Account".into()), - addrspec: model::AddrSpec { - local_part: "smith".into(), - domain: "home.example".into(), - }, - })]) - ) - }, - ) - } - - #[test] - fn test_to() { - lazy_eager( - "To: A Group:Ed Jones ,joe@where.test,John ;\r\n", - |to| { - assert_eq!( - to, - &eager::Field::To(vec![model::AddressRef::Many(model::GroupRef { - name: "A Group".into(), - participants: vec![ - model::MailboxRef { - name: Some("Ed Jones".into()), - addrspec: model::AddrSpec { - local_part: "c".into(), - domain: "a.test".into() - }, - }, - model::MailboxRef { - name: None, - addrspec: model::AddrSpec { - local_part: "joe".into(), - domain: "where.test".into() - }, - }, - model::MailboxRef { - name: Some("John".into()), - addrspec: model::AddrSpec { - local_part: "jdoe".into(), - domain: "one.test".into() - }, - }, - ] - })]) - ) - }, - ) - } - - #[test] - fn test_cc() { - lazy_eager("Cc: Undisclosed recipients:;\r\n", |cc| { - assert_eq!( - cc, - &eager::Field::Cc(vec![model::AddressRef::Many(model::GroupRef { - name: "Undisclosed recipients".into(), - participants: vec![], - })]), - ) - }) - } - - #[test] - fn test_bcc() { - lazy_eager("Bcc: (empty)\r\n", |bcc| { - assert_eq!(bcc, &eager::Field::Bcc(vec![]),) - }); - - lazy_eager("Bcc: \r\n", |bcc| { - assert_eq!(bcc, &eager::Field::Bcc(vec![]),) - }); - } - - #[test] - fn test_message_id() { - lazy_eager("Message-ID: <310@[127.0.0.1]>\r\n", |msg_id| { - assert_eq!( - msg_id, - &eager::Field::MessageID(model::MessageId { - left: "310", - right: "127.0.0.1" - },) - ) - }) - } - - #[test] - fn test_in_reply_to() { - lazy_eager("In-Reply-To: \r\n", |irt| { - assert_eq!( - irt, - &eager::Field::InReplyTo(vec![ - model::MessageId { - left: "a", - right: "b" - }, - model::MessageId { - left: "c", - right: "example.com" - }, - ]) - ) - }) - } - - #[test] - fn test_references() { - lazy_eager( - "References: <1234@local.machine.example> <3456@example.net>\r\n", - |refer| { - assert_eq!( - refer, - &eager::Field::References(vec![ - model::MessageId { - left: "1234", - right: "local.machine.example" - }, - model::MessageId { - left: "3456", - right: "example.net" - }, - ]) - ) - }, - ) - } - - #[test] - fn test_subject() { - lazy_eager("Subject: Aérogramme\r\n", |subject| { - assert_eq!( - subject, - &eager::Field::Subject(misc_token::Unstructured("Aérogramme".into())) - ) - }) - } - - #[test] - fn test_comments() { - lazy_eager("Comments: 😛 easter egg!\r\n", |comments| { - assert_eq!( - comments, - &eager::Field::Comments(misc_token::Unstructured("😛 easter egg!".into())), - ) - }) - } - - #[test] - fn test_keywords() { - lazy_eager( - "Keywords: fantasque, farfelu, fanfreluche\r\n", - |keywords| { - assert_eq!( - keywords, - &eager::Field::Keywords(misc_token::PhraseList(vec![ - "fantasque".into(), - "farfelu".into(), - "fanfreluche".into() - ])) - ) - }, - ) - } - - //@FIXME non ported tests: - - /* - #[test] - fn test_invalid_field_name() { - assert!(known_field("Unknown: unknown\r\n").is_err()); - } - - #[test] - fn test_rescue_field() { - assert_eq!( - rescue_field("Héron: élan\r\n\tnoël: test\r\nFrom: ..."), - Ok(("From: ...", Field::Rescue("Héron: élan\r\n\tnoël: test"))), - ); - } - - #[test] - fn test_wrong_fields() { - let fullmail = r#"Return-Path: xoxo - From: !!!! - - Hello world"#; - assert_eq!( - section(fullmail), - Ok(("Hello world", HeaderSection { - bad_fields: vec![ - Field::ReturnPath(FieldBody::Failed("xoxo")), - Field::From(FieldBody::Failed("!!!!")), - ], - ..Default::default() - })) - ); - } - */ -} diff --git a/src/old.multipass/field_lazy.rs b/src/old.multipass/field_lazy.rs deleted file mode 100644 index 95c662a..0000000 --- a/src/old.multipass/field_lazy.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::fragments::lazy; -use crate::multipass::extract_fields; -use crate::multipass::field_eager; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub fields: Vec>, - pub body: &'a [u8], -} - -pub fn new<'a>(ef: &'a extract_fields::Parsed<'a>) -> Parsed<'a> { - Parsed { - fields: ef.fields.iter().map(|e| (*e).into()).collect(), - body: ef.body, - } -} - -impl<'a> Parsed<'a> { - pub fn body(&'a self) -> field_eager::Parsed<'a> { - field_eager::new(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_field_name() { - assert_eq!( - new(&extract_fields::Parsed { - fields: vec![ - "From: hello@world.com,\r\n\talice@wonderlands.com\r\n", - "Date: 12 Mar 1997 07:33:25 Z\r\n", - ], - body: b"Hello world!", - }), - Parsed { - fields: vec![ - lazy::Field::From(lazy::MailboxList( - "hello@world.com,\r\n\talice@wonderlands.com\r\n" - )), - lazy::Field::Date(lazy::DateTime("12 Mar 1997 07:33:25 Z\r\n")), - ], - body: b"Hello world!", - } - ); - } - - #[test] - fn test_mime_fields() { - assert_eq!( - new(&extract_fields::Parsed { - fields: vec![ - "MIME-Version: 1.0 \r\n", - "Content-Type: multipart/alternative; boundary=\"bound\"\r\n", - "Content-Transfer-Encoding: 7bit\r\n", - "Content-ID: \r\n", - "Content-Description: hello world\r\n", - ], - body: b"Hello world!", - }), - Parsed { - fields: vec![ - lazy::Field::MIMEVersion(lazy::Version("1.0 \r\n")), - lazy::Field::MIME(lazy::MIMEField::ContentType(lazy::Type("multipart/alternative; boundary=\"bound\"\r\n"))), - lazy::Field::MIME(lazy::MIMEField::ContentTransferEncoding(lazy::Mechanism("7bit\r\n"))), - lazy::Field::MIME(lazy::MIMEField::ContentID(lazy::Identifier("\r\n"))), - lazy::Field::MIME(lazy::MIMEField::ContentDescription(lazy::Unstructured("hello world\r\n"))), - ], - body: b"Hello world!", - } - ); - } -} diff --git a/src/old.multipass/guess_charset.rs b/src/old.multipass/guess_charset.rs deleted file mode 100644 index 10a7fe7..0000000 --- a/src/old.multipass/guess_charset.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::borrow::Cow; - -use crate::error::IMFError; -use crate::fragments::encoding; -use crate::multipass::extract_fields; -use crate::multipass::segment; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub header: Cow<'a, str>, - pub body: &'a [u8], -} - -pub fn new<'a>(seg: &'a segment::Parsed<'a>) -> Parsed<'a> { - Parsed { - header: encoding::header_decode(&seg.header), - body: seg.body, - } -} - -impl<'a> Parsed<'a> { - pub fn fields(&'a self) -> Result, IMFError<'a>> { - extract_fields::new(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_charset() { - assert_eq!( - new(&segment::Parsed { - body: b"Hello world!", - header: b"From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n", - }), - Parsed { - header: "From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n".into(), - encoding: encoding_rs::UTF_8, - malformed: false, - body: b"Hello world!", - } - ); - } -} diff --git a/src/old.multipass/header_section.rs b/src/old.multipass/header_section.rs deleted file mode 100644 index cc2000a..0000000 --- a/src/old.multipass/header_section.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::fragments::section::Section; -use crate::multipass::{field_eager, body_structure}; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub fields: Section<'a>, - pub body: &'a [u8], -} - -pub fn new<'a>(p: &'a field_eager::Parsed<'a>) -> Parsed<'a> { - Parsed { - fields: Section::from_iter(p.fields.iter()), - body: p.body, - } -} - -impl<'a> Parsed<'a> { - pub fn body_structure(&self) -> body_structure::Parsed<'a> { - todo!() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::fragments::eager; - use crate::fragments::model; - use chrono::{FixedOffset, TimeZone}; - - #[test] - fn test_section() { - assert_eq!( - new(&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!", - } - ); - } -} diff --git a/src/old.multipass/mod.rs b/src/old.multipass/mod.rs deleted file mode 100644 index e8ad9e9..0000000 --- a/src/old.multipass/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod extract_fields; -pub mod field_eager; -pub mod field_lazy; -pub mod guess_charset; -pub mod header_section; -pub mod segment; -pub mod body_structure; diff --git a/src/old.multipass/segment.rs b/src/old.multipass/segment.rs deleted file mode 100644 index 71f3656..0000000 --- a/src/old.multipass/segment.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::error::IMFError; -use crate::multipass::guess_charset; -use crate::fragments::whitespace::headers; - -#[derive(Debug, PartialEq)] -pub struct Parsed<'a> { - pub header: &'a [u8], - pub body: &'a [u8], -} - -pub fn new<'a>(buffer: &'a [u8]) -> Result, IMFError<'a>> { - headers(buffer) - .map_err(|e| IMFError::Segment(e)) - .map(|(body, header)| Parsed { header, body }) -} - -impl<'a> Parsed<'a> { - pub fn charset(&'a self) -> guess_charset::Parsed<'a> { - guess_charset::new(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_segment() { - assert_eq!( - new(&b"From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n\r\nHello world!"[..]), - Ok(Parsed { - header: b"From: hello@world.com\r\nDate: 12 Mar 1997 07:33:25 Z\r\n", - body: b"Hello world!", - }) - ); - } -} diff --git a/src/part/part.rs b/src/part/part.rs index 5c7a3ce..710e82c 100644 --- a/src/part/part.rs +++ b/src/part/part.rs @@ -7,12 +7,14 @@ use nom::{ combinator::{not, opt, recognize}, }; -use crate::fragments::mime::{Mechanism, Type}; -use crate::fragments::model::MessageId; -use crate::fragments::misc_token::Unstructured; -use crate::fragments::whitespace::{CRLF, headers, line, obs_crlf}; -use crate::fragments::{eager,lazy}; -use crate::fragments::section::MIMESection; + +pub struct Part<'a, T> { + +} + +impl<'a> Part<'a, r#type::Text<'a>> { + +}