diff --git a/README.md b/README.md index fa5f4f2..c2ca75f 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ This library does not aim at implementing a specific RFC, but to be a swiss-army - Maintainability - modifying the code does not create regression and is possible for someone exterior to the project. - Compatibility - always try to parse something, do not panic or return an error. - Exhaustivity - serve as a common project to encode knowledge about emails (existing mime types, existing headers, etc.). -- Type safe - do not manipulate only strings/bytes but leverage Rust type system instead so you benefit of its safety checks at compile time. +- Type safe - do not manipulate only strings/bytes but leverage Rust type system instead so you benefit from its safety checks at compile time. [See more about this library goals in the doc/ folder](./doc/goals.md) diff --git a/src/mime/field.rs b/src/mime/field.rs index 6f61cfd..a9d52e5 100644 --- a/src/mime/field.rs +++ b/src/mime/field.rs @@ -8,7 +8,7 @@ use nom::{ use crate::header::{field_name, CompFieldList}; use crate::imf::identification::{msg_id, MessageID}; use crate::mime::mechanism::{mechanism, Mechanism}; -use crate::mime::AnyMIME; +use crate::mime::{AnyMIMEWithDefault, WithDefaultType}; use crate::mime::r#type::{naive_type, NaiveType}; use crate::text::misc_token::{unstructured, Unstructured}; use crate::text::whitespace::obs_crlf; @@ -49,8 +49,8 @@ impl<'a> Content<'a> { } impl<'a> CompFieldList<'a, Content<'a>> { - pub fn to_mime(self) -> AnyMIME<'a> { - self.known().into_iter().collect::() + pub fn to_mime (self) -> AnyMIMEWithDefault<'a, T> { + self.known().into_iter().collect::>() } } diff --git a/src/mime/mod.rs b/src/mime/mod.rs index 5b0a314..a4c625b 100644 --- a/src/mime/mod.rs +++ b/src/mime/mod.rs @@ -10,6 +10,8 @@ pub mod mechanism; /// Content-Type representation pub mod r#type; +use std::marker::PhantomData; + use crate::imf::identification::MessageID; use crate::mime::field::Content; use crate::mime::mechanism::Mechanism; @@ -47,10 +49,43 @@ impl<'a> AnyMIME<'a> { } } -impl<'a> FromIterator> for AnyMIME<'a> { +impl<'a, T: WithDefaultType> From> for AnyMIME<'a> { + fn from(a: AnyMIMEWithDefault<'a, T>) -> Self { + a.0 + } +} + +#[derive(Debug, PartialEq, Default, Clone)] +pub struct Generic<'a> { + pub transfer_encoding: Mechanism<'a>, + pub id: Option>, + pub description: Option>, +} + +pub trait WithDefaultType { + fn default_type() -> AnyType; +} + +pub struct WithGenericDefault {} +impl WithDefaultType for WithGenericDefault { + fn default_type() -> AnyType { + AnyType::Text(r#type::Text::default()) + } +} +pub struct WithDigestDefault {} +impl WithDefaultType for WithDigestDefault { + fn default_type() -> AnyType { + AnyType::Message(r#type::Message::default()) + } +} + +#[derive(Debug, PartialEq)] +pub struct AnyMIMEWithDefault<'a, T: WithDefaultType>(pub AnyMIME<'a>, PhantomData); + +impl<'a, T: WithDefaultType> FromIterator> for AnyMIMEWithDefault<'a, T> { fn from_iter>>(it: I) -> Self { let (at, gen) = it.into_iter().fold( - (AnyType::default(), Generic::default()), + (T::default_type(), Generic::default()), |(mut at, mut section), field| { match field { Content::Type(v) => at = v.to_type(), @@ -62,13 +97,6 @@ impl<'a> FromIterator> for AnyMIME<'a> { }, ); - Self::from_pair(at, gen) + Self(AnyMIME::from_pair(at, gen), PhantomData) } } - -#[derive(Debug, PartialEq, Default, Clone)] -pub struct Generic<'a> { - pub transfer_encoding: Mechanism<'a>, - pub id: Option>, - pub description: Option>, -} diff --git a/src/mime/type.rs b/src/mime/type.rs index 88a21ff..51934bb 100644 --- a/src/mime/type.rs +++ b/src/mime/type.rs @@ -57,17 +57,12 @@ pub enum AnyType { Binary(Binary), } -impl Default for AnyType { - fn default() -> Self { - Self::Text(Text::default()) - } -} impl<'a> From<&'a NaiveType<'a>> for AnyType { fn from(nt: &'a NaiveType<'a>) -> Self { match nt.main.to_ascii_lowercase().as_slice() { b"multipart" => Multipart::try_from(nt) .map(Self::Multipart) - .unwrap_or(Self::default()), + .unwrap_or(Self::Text(Text::default())), b"message" => Self::Message(Message::from(nt)), b"text" => Self::Text(Text::from(nt)), _ => Self::Binary(Binary::default()), diff --git a/src/part/composite.rs b/src/part/composite.rs index 4b76a51..af4c485 100644 --- a/src/part/composite.rs +++ b/src/part/composite.rs @@ -47,7 +47,10 @@ pub fn multipart<'a>( // parse mime headers let (input, fields) = header(mime::field::content)(input)?; - let mime = fields.to_mime(); + let mime = match m.0.subtype { + mime::r#type::MultipartSubtype::Digest => fields.to_mime::().into(), + _ => fields.to_mime::().into(), + }; // parse raw part let (input, rpart) = part::part_raw(bound)(input)?; @@ -75,7 +78,7 @@ pub fn message<'a>( move |input: &[u8]| { let (input, fields): (_, CompFieldList) = header(part::field::mixed_field)(input)?; - let (in_mime, imf) = fields.sections(); + let (in_mime, imf) = fields.sections::(); let part = part::to_anypart(in_mime, input); diff --git a/src/part/field.rs b/src/part/field.rs index 5eed681..542390d 100644 --- a/src/part/field.rs +++ b/src/part/field.rs @@ -36,19 +36,19 @@ impl<'a> MixedField<'a> { } } impl<'a> CompFieldList<'a, MixedField<'a>> { - pub fn sections(self) -> (mime::AnyMIME<'a>, imf::Imf<'a>) { + pub fn sections(self) -> (mime::AnyMIME<'a>, imf::Imf<'a>) { let k = self.known(); let (v1, v2): (Vec, Vec) = k.into_iter().partition(|v| v.mime().is_some()); let mime = v1 .into_iter() .filter_map(|v| v.to_mime()) - .collect::(); + .collect::>(); let imf = v2 .into_iter() .filter_map(|v| v.to_imf()) .collect::(); - (mime, imf) + (mime.into(), imf) } } pub fn mixed_field(input: &[u8]) -> IResult<&[u8], MixedField> {