diff --git a/src/core/builder.rs b/src/core/builder.rs index 9e1ca84..c2ad802 100644 --- a/src/core/builder.rs +++ b/src/core/builder.rs @@ -17,7 +17,7 @@ impl MessageParser { pub fn new() -> Self { Self { header_map: Default::default(), - def_hdr_parse_fnc: |s| s.parse_raw(), + def_hdr_parse_fnc: |s| s.parse_raw().into(), } } @@ -122,19 +122,21 @@ impl MessageParser { /// Parse a header as text decoding RFC 2047 encoded words. pub fn header_text(mut self, header: impl Into>) -> Self { self.header_map - .insert(header.into(), |s| s.parse_unstructured()); + .insert(header.into(), |s| s.parse_unstructured().into()); self } /// Parse a header as a RFC 5322 date. pub fn header_date(mut self, header: impl Into>) -> Self { - self.header_map.insert(header.into(), |s| s.parse_date()); + self.header_map + .insert(header.into(), |s| s.parse_date().into()); self } /// Parse a header as an address. pub fn header_address(mut self, header: impl Into>) -> Self { - self.header_map.insert(header.into(), |s| s.parse_address()); + self.header_map + .insert(header.into(), |s| s.parse_address().into()); self } @@ -147,7 +149,7 @@ impl MessageParser { /// Parse a header as a MIME `Content-Type` or `Content-Disposition` type. pub fn header_content_type(mut self, header: impl Into>) -> Self { self.header_map - .insert(header.into(), |s| s.parse_content_type()); + .insert(header.into(), |s| s.parse_content_type().into()); self } @@ -161,13 +163,14 @@ impl MessageParser { /// Parse a header as a received header. pub fn header_received(mut self, header: impl Into>) -> Self { self.header_map - .insert(header.into(), |s| s.parse_received()); + .insert(header.into(), |s| s.parse_received().map(Box::new).into()); self } /// Parse a header as a raw string, no RFC 2047 decoding is done. pub fn header_raw(mut self, header: impl Into>) -> Self { - self.header_map.insert(header.into(), |s| s.parse_raw()); + self.header_map + .insert(header.into(), |s| s.parse_raw().into()); self } @@ -182,13 +185,13 @@ impl MessageParser { /// Parse all other headers as text decoding RFC 2047 encoded words. pub fn default_header_text(mut self) -> Self { - self.def_hdr_parse_fnc = |s| s.parse_unstructured(); + self.def_hdr_parse_fnc = |s| s.parse_unstructured().into(); self } /// Parse all other headers as raw strings, no RFC 2047 decoding is done. pub fn default_header_raw(mut self) -> Self { - self.def_hdr_parse_fnc = |s| s.parse_raw(); + self.def_hdr_parse_fnc = |s| s.parse_raw().into(); self } diff --git a/src/core/message.rs b/src/core/message.rs index e839302..81350a3 100644 --- a/src/core/message.rs +++ b/src/core/message.rs @@ -63,14 +63,18 @@ impl<'x> Message<'x> { HeaderForm::Raw => HeaderValue::Text( std::str::from_utf8(bytes).unwrap_or_default().trim().into(), ), - HeaderForm::Text => MessageStream::new(bytes).parse_unstructured(), - HeaderForm::Addresses => MessageStream::new(bytes).parse_address(), + HeaderForm::Text => { + MessageStream::new(bytes).parse_unstructured().into() + } + HeaderForm::Addresses => { + MessageStream::new(bytes).parse_address().into() + } HeaderForm::GroupedAddresses => { - MessageStream::new(bytes).parse_address() + MessageStream::new(bytes).parse_address().into() } HeaderForm::MessageIds => MessageStream::new(bytes).parse_id(), - HeaderForm::Date => MessageStream::new(bytes).parse_date(), - HeaderForm::URLs => MessageStream::new(bytes).parse_address(), + HeaderForm::Date => MessageStream::new(bytes).parse_date().into(), + HeaderForm::URLs => MessageStream::new(bytes).parse_address().into(), }), ); } diff --git a/src/lib.rs b/src/lib.rs index ea84deb..91edc7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,50 @@ pub mod decoders; pub mod mailbox; pub mod parsers; +impl<'x> From>> for HeaderValue<'x> { + fn from(src: Option>) -> HeaderValue<'x> { + match src { + Some(x) => HeaderValue::Address(x), + None => HeaderValue::Empty, + } + } +} +impl<'x> From>> for HeaderValue<'x> { + fn from(src: Option>) -> HeaderValue<'x> { + match src { + Some(x) => HeaderValue::Text(x), + None => HeaderValue::Empty, + } + } +} + +impl<'x> From> for HeaderValue<'x> { + fn from(src: Option) -> HeaderValue<'x> { + match src { + Some(x) => HeaderValue::DateTime(x), + None => HeaderValue::Empty, + } + } +} + +impl<'x> From>> for HeaderValue<'x> { + fn from(src: Option>) -> HeaderValue<'x> { + match src { + Some(x) => HeaderValue::ContentType(x), + None => HeaderValue::Empty, + } + } +} + +impl<'x> From>>> for HeaderValue<'x> { + fn from(src: Option>>) -> HeaderValue<'x> { + match src { + Some(x) => HeaderValue::Received(x), + None => HeaderValue::Empty, + } + } +} + use parsers::MessageStream; use std::{borrow::Cow, collections::HashMap, hash::Hash, net::IpAddr}; diff --git a/src/parsers/fields/address.rs b/src/parsers/fields/address.rs index b82fe21..f68436a 100644 --- a/src/parsers/fields/address.rs +++ b/src/parsers/fields/address.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; -use crate::{parsers::MessageStream, Addr, Address, Group, HeaderValue}; +use crate::{parsers::MessageStream, Addr, Address, Group}; #[derive(PartialEq, Clone, Copy, Debug)] enum AddressState { @@ -191,7 +191,7 @@ impl<'x> AddressParser<'x> { } impl<'x> MessageStream<'x> { - pub fn parse_address(&mut self) -> HeaderValue<'x> { + pub fn parse_address(&mut self) -> Option> { let mut parser = AddressParser { token_start: 0, token_end: 0, @@ -354,11 +354,11 @@ impl<'x> MessageStream<'x> { if parser.group_name.is_some() || !parser.result.is_empty() { parser.add_group(); - HeaderValue::Address(Address::Group(parser.result)) + Some(Address::Group(parser.result)) } else if !parser.addresses.is_empty() { - HeaderValue::Address(Address::List(parser.addresses)) + Some(Address::List(parser.addresses)) } else { - HeaderValue::Empty + None } } } @@ -468,7 +468,7 @@ mod tests { assert_eq!( MessageStream::new(test.header.as_bytes()) .parse_address() - .unwrap_address(), + .unwrap(), test.expected, "failed for {:?}", test.header diff --git a/src/parsers/fields/content_type.rs b/src/parsers/fields/content_type.rs index 234262f..a4252d3 100644 --- a/src/parsers/fields/content_type.rs +++ b/src/parsers/fields/content_type.rs @@ -9,7 +9,7 @@ use std::borrow::Cow; use crate::{ decoders::{charsets::map::charset_decoder, hex::decode_hex}, parsers::MessageStream, - Attribute, ContentType, HeaderValue, + Attribute, ContentType, }; #[derive(Clone, Copy, PartialEq, Debug)] @@ -252,7 +252,7 @@ impl<'x> ContentTypeParser<'x> { } impl<'x> MessageStream<'x> { - pub fn parse_content_type(&mut self) -> HeaderValue<'x> { + pub fn parse_content_type(&mut self) -> Option> { let mut parser = ContentTypeParser { state: ContentState::Type, state_stack: Vec::new(), @@ -345,7 +345,7 @@ impl<'x> MessageStream<'x> { } return if let Some(content_type) = parser.c_type { - HeaderValue::ContentType(ContentType { + Some(ContentType { c_type: content_type, c_subtype: parser.c_subtype.take(), attributes: if !parser.attributes.is_empty() { @@ -355,7 +355,7 @@ impl<'x> MessageStream<'x> { }, }) } else { - HeaderValue::Empty + None }; } } @@ -513,7 +513,7 @@ impl<'x> MessageStream<'x> { } } - HeaderValue::Empty + None } } #[cfg(test)] @@ -523,14 +523,8 @@ mod tests { #[test] fn parse_content_fields() { for test in load_tests("content_type.json") { - assert_eq!( - MessageStream::new(test.header.as_bytes()) - .parse_content_type() - .into_content_type(), - test.expected, - "failed for {:?}", - test.header - ); + let content_type = MessageStream::new(test.header.as_bytes()).parse_content_type(); + assert_eq!(content_type, test.expected, "failed for {:?}", test.header); } /*let mut builder = crate::parsers::fields::TestBuilder::new("content_type.json"); diff --git a/src/parsers/fields/date.rs b/src/parsers/fields/date.rs index 51925a1..d29a6df 100644 --- a/src/parsers/fields/date.rs +++ b/src/parsers/fields/date.rs @@ -6,7 +6,7 @@ use std::fmt; -use crate::{parsers::MessageStream, DateTime, HeaderValue}; +use crate::{parsers::MessageStream, DateTime}; pub static DOW: &[&str] = &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; pub static MONTH: &[&str] = &[ @@ -16,10 +16,7 @@ pub static MONTH: &[&str] = &[ impl DateTime { /// Parses an RFC822 date pub fn parse_rfc822(value: &str) -> Option { - match MessageStream::new(value.as_bytes()).parse_date() { - HeaderValue::DateTime(dt) => dt.into(), - _ => None, - } + MessageStream::new(value.as_bytes()).parse_date() } /// Parses an RFC3339 date @@ -286,7 +283,7 @@ impl fmt::Display for DateTime { } impl<'x> MessageStream<'x> { - pub fn parse_date(&mut self) -> HeaderValue<'x> { + pub fn parse_date(&mut self) -> Option { let mut pos = 0; let mut parts = [0u32; 7]; let mut parts_sizes = [ @@ -416,7 +413,7 @@ impl<'x> MessageStream<'x> { } if pos >= 6 { - HeaderValue::DateTime(DateTime { + Some(DateTime { year: if (0..=49).contains(&parts[2]) { parts[2] + 2000 } else if (50..=99).contains(&parts[2]) { @@ -438,7 +435,7 @@ impl<'x> MessageStream<'x> { tz_before_gmt: !is_plus, }) } else { - HeaderValue::Empty + None } } // 4.3 obsolete date and time @@ -496,9 +493,7 @@ mod tests { #[test] fn parse_dates() { for test in load_tests("date.json") { - let datetime = MessageStream::new(test.header.as_bytes()) - .parse_date() - .into_datetime(); + let datetime = MessageStream::new(test.header.as_bytes()).parse_date(); assert_eq!(datetime, test.expected, "failed for {:?}", test.header); match datetime { diff --git a/src/parsers/fields/raw.rs b/src/parsers/fields/raw.rs index e81dcb4..c6ca9db 100644 --- a/src/parsers/fields/raw.rs +++ b/src/parsers/fields/raw.rs @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT */ -use crate::{parsers::MessageStream, HeaderValue}; +use crate::parsers::MessageStream; impl<'x> MessageStream<'x> { - pub fn parse_raw(&mut self) -> HeaderValue<'x> { + pub fn parse_raw(&mut self) -> Option> { let mut token_start: usize = 0; let mut token_end: usize = 0; @@ -32,11 +32,11 @@ impl<'x> MessageStream<'x> { } if token_start > 0 { - HeaderValue::Text(String::from_utf8_lossy( + Some(String::from_utf8_lossy( self.bytes(token_start - 1..token_end), )) } else { - HeaderValue::Empty + None } } @@ -75,9 +75,7 @@ mod tests { for (input, expected) in inputs { assert_eq!( - MessageStream::new(input.as_bytes()) - .parse_raw() - .unwrap_text(), + MessageStream::new(input.as_bytes()).parse_raw().unwrap(), expected, "Failed for '{:?}'", input diff --git a/src/parsers/fields/received.rs b/src/parsers/fields/received.rs index 2f384f8..e579921 100644 --- a/src/parsers/fields/received.rs +++ b/src/parsers/fields/received.rs @@ -6,9 +6,7 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::{ - parsers::MessageStream, DateTime, Greeting, HeaderValue, Host, Protocol, Received, TlsVersion, -}; +use crate::{parsers::MessageStream, DateTime, Greeting, Host, Protocol, Received, TlsVersion}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Token { @@ -90,7 +88,7 @@ enum State { } impl<'x> MessageStream<'x> { - pub fn parse_received(&mut self) -> HeaderValue<'x> { + pub fn parse_received(&mut self) -> Option> { //let c = print!("-> {}", std::str::from_utf8(self.data).unwrap()); let mut tokenizer = Tokenizer::new(self).peekable(); @@ -353,9 +351,9 @@ impl<'x> MessageStream<'x> { || received.via.is_some() || received.date.is_some() { - HeaderValue::Received(Box::new(received)) + Some(received) } else { - HeaderValue::Empty + None } } } @@ -824,7 +822,7 @@ mod tests { assert_eq!( MessageStream::new(test.header.as_bytes()) .parse_received() - .unwrap_received(), + .unwrap(), test.expected, "failed for {:?}", test.header diff --git a/src/parsers/fields/unstructured.rs b/src/parsers/fields/unstructured.rs index b92367d..0ee8152 100644 --- a/src/parsers/fields/unstructured.rs +++ b/src/parsers/fields/unstructured.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; -use crate::{parsers::MessageStream, HeaderValue}; +use crate::parsers::MessageStream; struct UnstructuredParser<'x> { token_start: usize, token_end: usize, @@ -39,7 +39,7 @@ impl<'x> UnstructuredParser<'x> { } impl<'x> MessageStream<'x> { - pub fn parse_unstructured(&mut self) -> HeaderValue<'x> { + pub fn parse_unstructured(&mut self) -> Option> { let mut parser = UnstructuredParser { token_start: 0, token_end: 0, @@ -54,9 +54,9 @@ impl<'x> MessageStream<'x> { if !self.try_next_is_space() { return match parser.tokens.len() { - 1 => HeaderValue::Text(parser.tokens.pop().unwrap()), - 0 => HeaderValue::Empty, - _ => HeaderValue::Text(parser.tokens.concat().into()), + 1 => Some(parser.tokens.pop().unwrap()), + 0 => None, + _ => Some(parser.tokens.concat().into()), }; } else { continue; @@ -84,7 +84,7 @@ impl<'x> MessageStream<'x> { parser.token_end = self.offset(); } - HeaderValue::Empty + None } } @@ -98,7 +98,7 @@ mod tests { assert_eq!( MessageStream::new(test.header.as_bytes()) .parse_unstructured() - .unwrap_text(), + .unwrap(), test.expected, "failed for {:?}", test.header diff --git a/src/parsers/header.rs b/src/parsers/header.rs index d160018..5a3f0f2 100644 --- a/src/parsers/header.rs +++ b/src/parsers/header.rs @@ -39,7 +39,7 @@ impl<'x> MessageStream<'x> { | HeaderName::Comments | HeaderName::ContentDescription | HeaderName::ContentLocation - | HeaderName::ContentTransferEncoding => self.parse_unstructured(), + | HeaderName::ContentTransferEncoding => self.parse_unstructured().into(), HeaderName::From | HeaderName::To | HeaderName::Cc @@ -57,8 +57,8 @@ impl<'x> MessageStream<'x> { | HeaderName::ListOwner | HeaderName::ListPost | HeaderName::ListSubscribe - | HeaderName::ListUnsubscribe => self.parse_address(), - HeaderName::Date | HeaderName::ResentDate => self.parse_date(), + | HeaderName::ListUnsubscribe => self.parse_address().into(), + HeaderName::Date | HeaderName::ResentDate => self.parse_date().into(), HeaderName::MessageId | HeaderName::References | HeaderName::InReplyTo @@ -68,12 +68,12 @@ impl<'x> MessageStream<'x> { HeaderName::Keywords | HeaderName::ContentLanguage => { self.parse_comma_separared() } - HeaderName::Received => self.parse_received(), - HeaderName::MimeVersion => self.parse_raw(), + HeaderName::Received => self.parse_received().map(Box::new).into(), + HeaderName::MimeVersion => self.parse_raw().into(), HeaderName::ContentType | HeaderName::ContentDisposition => { - self.parse_content_type() + self.parse_content_type().into() } - _ => self.parse_raw(), + _ => self.parse_raw().into(), } } else { (conf