summaryrefslogtreecommitdiffstats
path: root/headers/src
diff options
context:
space:
mode:
authorPhilipp Korber <p.korber@1aim.com>2018-11-16 15:46:43 +0100
committerPhilipp Korber <p.korber@1aim.com>2018-11-16 15:46:43 +0100
commit652d6f0ffeee7302a2cb51059bef75d8b0bb50be (patch)
treec3851592642938172f280f7428d43e08b0fe2cbe /headers/src
parent0947fe8996149fe20a6d47a793f9555790eb2eae (diff)
refactor: merged sources of mail-headers,mail-internals,mail-core, mail
Originally it was palaned to do a merge with `--allow-unrelated-history` but this can not be doesn as `mail-core` has a "invalid" history which has a merge conflict **with itself**. So even rewinding the history on a empty repo is not possible. Instead the code was directly coppied over losing history. But the history is still available in the different `history-backup-*` branches. It is just that the past history is decoupled from the current history.
Diffstat (limited to 'headers/src')
-rw-r--r--headers/src/convert.rs37
-rw-r--r--headers/src/data/inner_item.rs251
-rw-r--r--headers/src/data/input.rs131
-rw-r--r--headers/src/data/mod.rs23
-rw-r--r--headers/src/data/simple_item.rs113
-rw-r--r--headers/src/error.rs205
-rw-r--r--headers/src/header.rs253
-rw-r--r--headers/src/header_components/cfws.rs64
-rw-r--r--headers/src/header_components/date_time.rs90
-rw-r--r--headers/src/header_components/disposition.rs308
-rw-r--r--headers/src/header_components/email.rs404
-rw-r--r--headers/src/header_components/file_meta.rs80
-rw-r--r--headers/src/header_components/mailbox.rs212
-rw-r--r--headers/src/header_components/mailbox_list.rs300
-rw-r--r--headers/src/header_components/media_type.rs513
-rw-r--r--headers/src/header_components/message_id.rs353
-rw-r--r--headers/src/header_components/mod.rs66
-rw-r--r--headers/src/header_components/path.rs73
-rw-r--r--headers/src/header_components/phrase.rs127
-rw-r--r--headers/src/header_components/phrase_list.rs157
-rw-r--r--headers/src/header_components/raw_unstructured.rs87
-rw-r--r--headers/src/header_components/received_token.rs97
-rw-r--r--headers/src/header_components/transfer_encoding.rs117
-rw-r--r--headers/src/header_components/unstructured.rs194
-rw-r--r--headers/src/header_components/utils/mod.rs90
-rw-r--r--headers/src/header_components/utils/text_partition.rs62
-rw-r--r--headers/src/header_components/word.rs222
-rw-r--r--headers/src/header_macro.rs159
-rw-r--r--headers/src/headers.rs276
-rw-r--r--headers/src/lib.rs170
-rw-r--r--headers/src/macros.rs80
-rw-r--r--headers/src/map/into_iter.rs18
-rw-r--r--headers/src/map/mod.rs1042
-rw-r--r--headers/src/name.rs182
34 files changed, 6556 insertions, 0 deletions
diff --git a/headers/src/convert.rs b/headers/src/convert.rs
new file mode 100644
index 0000000..1153965
--- /dev/null
+++ b/headers/src/convert.rs
@@ -0,0 +1,37 @@
+//TODO potentially move HeaderTryFrom to `mail-headers`
+use error::ComponentCreationError;
+
+//TODO replace with std TryFrom once it is stable
+// (either a hard replace, or a soft replace which implements HeaderTryFrom if TryFrom exist)
+/// Workaround for `TryFrom`,`TryInto` not being stable.
+pub trait HeaderTryFrom<T>: Sized {
+ fn try_from(val: T) -> Result<Self, ComponentCreationError>;
+}
+
+/// Workaround for `TryFrom`,`TryInto` not being stable.
+pub trait HeaderTryInto<T>: Sized {
+ fn try_into(self) -> Result<T, ComponentCreationError>;
+}
+
+impl<F, T> HeaderTryInto<T> for F where T: HeaderTryFrom<F> {
+ fn try_into(self) -> Result<T, ComponentCreationError> {
+ T::try_from(self)
+ }
+}
+
+
+impl<T> HeaderTryFrom<T> for T {
+ fn try_from(val: T) -> Result<Self, ComponentCreationError> {
+ Ok( val )
+ }
+}
+
+// It is not possible to auto-implement HeaderTryFrom for From/Into as
+// this will make new HeaderTryFrom implementations outside of this care
+// nearly impossible making the trait partially useless
+//
+//impl<T, F> HeaderTryFrom<F> for T where F: Into<T> {
+// fn try_from(val: F) -> Result<T, Error> {
+// Ok( val.into() )
+// }
+//} \ No newline at end of file
diff --git a/headers/src/data/inner_item.rs b/headers/src/data/inner_item.rs
new file mode 100644
index 0000000..147a959
--- /dev/null
+++ b/headers/src/data/inner_item.rs
@@ -0,0 +1,251 @@
+use std::ops::Deref;
+use std::sync::Arc;
+use std::borrow::ToOwned;
+
+use owning_ref::OwningRef;
+use soft_ascii_string::{SoftAsciiString, SoftAsciiStr};
+
+#[cfg(feature="serde")]
+use serde::{Serialize, Deserialize, Serializer, Deserializer, de::Error as __Error};
+
+
+/// InnerAscii is string data container which can contain either a
+/// owned `SoftAsciiString` or a `SoftAsciiStr` reference into a shared
+/// string buffer.
+#[derive(Debug, Clone, Hash, Eq)]
+pub enum InnerAscii {
+ Owned(SoftAsciiString),
+ //by using String+SoftAsciiStr we can eliminate unessesary copies
+ Shared(OwningRef<Arc<String>, SoftAsciiStr>)
+}
+
+impl InnerAscii {
+
+ /// converts this container into on which uses underlying shared data
+ ///
+ /// if the data is already shared nothing is done.
+ /// If not the owned data is converted into the underlying string buffer
+ /// and `OwningRef` is used to enable the shared reference
+ ///
+ /// Note that the underlying buffer is no an `SoftAsciiString` but a
+ /// `String` (from which we happend to know that it fulfills the "is
+ /// us-ascii" soft constraint). This allows us to have an `InnerAscii`
+ /// share data with a possible non us-ascii string buffer as long as
+ /// the part accessable through the `SoftAsciiStr` is ascii. Or at last
+ /// should be ascii as it's a soft constraint.
+ pub fn into_shared(self) -> Self {
+ match self {
+ InnerAscii::Owned(value) => {
+ let buffer: Arc<String> = Arc::new(value.into());
+ let orf = OwningRef::new(buffer).map(|data: &String| {
+ // we got it from a SoftAsciiString so no check here
+ SoftAsciiStr::from_unchecked(&**data)
+ });
+ InnerAscii::Shared(orf)
+ }
+ v => v
+ }
+ }
+}
+
+/// InnerUtf8 is string data container which can contain either a
+/// owned `String` or a `str` reference into a shared
+/// string buffer.
+#[derive(Debug, Clone, Hash, Eq)]
+pub enum InnerUtf8 {
+ Owned(String),
+ //by using String+SoftAsciiStr we can eliminate unessesary copies
+ Shared(OwningRef<Arc<String>, str>)
+}
+
+impl InnerUtf8 {
+
+ /// converts this container into on which uses underlying shared data
+ ///
+ /// if the data is already shared nothing is done.
+ /// If not the owned data is converted into the underlying string buffer
+ /// and `OwningRef` is used to enable the shared reference
+ pub fn into_shared(self) -> Self {
+ match self {
+ InnerUtf8::Owned(value) => {
+ let buffer = Arc::new(value);
+ let orf = OwningRef::new(buffer)
+ .map(|rced| &**rced);
+ InnerUtf8::Shared(orf)
+ }
+ v => v
+ }
+ }
+}
+
+
+macro_rules! inner_impl {
+ ($name:ident, $owned_form:ty, $borrowed_form:ty) => (
+ impl $name {
+ pub fn new<S: Into<$owned_form>>( data: S ) -> Self {
+ $name::Owned( data.into() )
+ }
+ }
+ impl From<$owned_form> for $name {
+ fn from( data: $owned_form ) -> Self {
+ Self::new( data )
+ }
+ }
+
+ impl Into<$owned_form> for $name {
+ fn into(self) -> $owned_form {
+ match self {
+ $name::Owned( owned ) => owned,
+ $name::Shared( shared ) => {
+ let as_ref: &$borrowed_form = &*shared;
+ as_ref.to_owned()
+ }
+ }
+ }
+ }
+
+ impl Deref for $name {
+ type Target = $borrowed_form;
+
+ fn deref( &self ) -> &$borrowed_form{
+ match *self {
+ $name::Owned( ref string ) => &*string,
+ $name::Shared( ref owning_ref ) => &*owning_ref
+ }
+ }
+ }
+
+ #[cfg(feature="serde")]
+ impl Serialize for $name {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where S: Serializer
+ {
+ let borrowed: &$borrowed_form = &*self;
+ let as_ref: &str = borrowed.as_ref();
+ serializer.serialize_str( as_ref )
+ }
+ }
+
+ impl PartialEq for $name {
+ fn eq(&self, other: &$name) -> bool {
+ let me: &$borrowed_form = &*self;
+ let other: &$borrowed_form = &*other;
+ me == other
+ }
+ }
+
+ impl AsRef<str> for $name {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+ }
+ );
+}
+
+inner_impl!{ InnerAscii, SoftAsciiString, SoftAsciiStr }
+inner_impl!{ InnerUtf8, String, str }
+//inner_impl!{ InnerOtherItem, OtherString, OtherStr }
+
+impl InnerAscii {
+ pub fn as_str( &self ) -> &str {
+ match *self {
+ InnerAscii::Owned( ref owned ) => owned.as_str(),
+ InnerAscii::Shared( ref shared ) => shared.as_str()
+ }
+ }
+}
+
+#[cfg(feature="serde")]
+impl<'de> Deserialize<'de> for InnerAscii {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where D: Deserializer<'de>
+ {
+ let content = String::deserialize(deserializer)
+ .map_err(|err| D::Error::custom(err))?;
+ let content = SoftAsciiString::from_string(content)
+ .map_err(|err| D::Error::custom(err))?;
+ Ok(InnerAscii::from(content))
+ }
+}
+
+impl InnerUtf8 {
+ pub fn as_str( &self ) -> &str {
+ match *self {
+ InnerUtf8::Owned( ref owned ) => owned.as_str(),
+ InnerUtf8::Shared( ref shared ) => &**shared
+ }
+ }
+}
+
+#[cfg(feature="serde")]
+impl<'de> Deserialize<'de> for InnerUtf8 {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where D: Deserializer<'de>
+ {
+ let content = String::deserialize(deserializer)
+ .map_err(|err| D::Error::custom(err))?;
+ Ok(InnerUtf8::from(content))
+ }
+}
+
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn inner_ascii_item_eq() {
+ let a = InnerAscii::Owned( SoftAsciiString::from_string( "same" ).unwrap() );
+ let b = InnerAscii::Shared(
+ OwningRef::new(Arc::new("same".to_owned()))
+ .map(|v| SoftAsciiStr::from_unchecked(&**v))
+ );
+ assert_eq!( a, b );
+ }
+
+ #[test]
+ fn inner_ascii_item_neq() {
+ let a = InnerAscii::Owned( SoftAsciiString::from_string( "same" ).unwrap() );
+ let b = InnerAscii::Shared(
+ OwningRef::new(Arc::new("not same".to_owned()))
+ .map(|v| SoftAsciiStr::from_unchecked(&**v))
+ );
+ assert_ne!( a, b );
+ }
+
+ #[test]
+ fn inner_utf8_item_eq() {
+ let a = InnerUtf8::Owned( String::from( "same" ) );
+ let b = InnerUtf8::Shared(
+ OwningRef::new(
+ Arc::new( String::from( "same" ) ) )
+ .map(|v| &**v)
+ );
+ assert_eq!( a, b );
+ }
+
+ #[test]
+ fn inner_utf8_item_neq() {
+ let a = InnerUtf8::Owned( String::from( "same" ) );
+ let b = InnerUtf8::Shared(
+ OwningRef::new(
+ Arc::new( String::from( "not same" ) ) )
+ .map(|v| &**v)
+ );
+ assert_ne!( a, b );
+ }
+
+ #[test]
+ fn has_as_str() {
+ use std::borrow::ToOwned;
+
+ assert_eq!(
+ "hy",
+ InnerAscii::Owned( SoftAsciiStr::from_unchecked("hy").to_owned() ).as_str()
+ );
+ assert_eq!(
+ "hy",
+ InnerUtf8::Owned( "hy".into() ).as_str()
+ );
+ }
+} \ No newline at end of file
diff --git a/headers/src/data/input.rs b/headers/src/data/input.rs
new file mode 100644
index 0000000..ad43d10
--- /dev/null
+++ b/headers/src/data/input.rs
@@ -0,0 +1,131 @@
+use std::result::{ Result as StdResult };
+use std::fmt::{self, Display};
+
+use soft_ascii_string::SoftAsciiString;
+
+use ::HeaderTryFrom;
+use ::error::ComponentCreationError;
+
+use super::inner_item::{ InnerUtf8, InnerAscii };
+
+/// a Input is similar to Item a container data container used in different
+/// context's with different restrictions, but different to an Item it
+/// might contain characters which require encoding (e.g. encoded words)
+/// to represent them
+#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+pub struct Input( pub InnerUtf8 );
+
+
+impl Input {
+
+ pub fn into_shared( self ) -> Self {
+ Input( self.0.into_shared() )
+ }
+
+
+ pub fn into_ascii_item( self ) -> StdResult<InnerAscii, Input> {
+ match self {
+ Input( InnerUtf8::Owned( string ) ) => {
+ match SoftAsciiString::from_string(string) {
+ Ok(asciied) => Ok(InnerAscii::Owned(asciied)),
+ Err(err) => Err(Input(InnerUtf8::Owned(err.into_source())))
+ }
+ }
+ Input( InnerUtf8::Shared( shared ) ) => {
+ if shared.is_ascii() {
+ Ok(InnerAscii::Owned(SoftAsciiString::from_unchecked(&*shared)))
+ } else {
+ Err(Input(InnerUtf8::Shared(shared)))
+ }
+ }
+ }
+ }
+
+ pub fn into_ascii_item_unchecked( self ) -> InnerAscii {
+ match self {
+ Input( InnerUtf8::Owned( string ) ) =>
+ InnerAscii::Owned( SoftAsciiString::from_unchecked( string ) ),
+ Input( InnerUtf8::Shared( shared ) ) =>
+ InnerAscii::Owned(
+ SoftAsciiString::from_unchecked(&*shared) )
+ }
+ }
+
+ pub fn into_utf8_item( self ) -> InnerUtf8 {
+ self.0
+ }
+}
+
+impl<'a> From<&'a str> for Input {
+ fn from( s: &'a str ) -> Self {
+ Input( InnerUtf8::Owned( s.into() ) )
+ }
+}
+
+impl From<String> for Input {
+ fn from( s: String ) -> Self {
+ Input( InnerUtf8::Owned( s ) )
+ }
+}
+
+impl<'a> HeaderTryFrom<&'a str> for Input
+{
+ fn try_from(val: &'a str) -> Result<Self, ComponentCreationError> {
+ Ok(val.into())
+ }
+}
+impl HeaderTryFrom<String> for Input
+{
+ fn try_from(val: String) -> Result<Self, ComponentCreationError> {
+ Ok(val.into())
+ }
+}
+
+impl Into<String> for Input {
+ fn into(self) -> String {
+ self.0.into()
+ }
+}
+
+impl Display for Input {
+ fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
+ fter.write_str(self.as_str())
+ }
+}
+
+deref0!( +mut Input => InnerUtf8 );
+
+
+
+#[cfg(test)]
+mod test {
+ use std::sync::Arc;
+ use owning_ref::OwningRef;
+
+ use super::*;
+
+ #[test]
+ fn input_eq() {
+ let a = Input( InnerUtf8::Owned( "same".into() ) );
+ let b = Input( InnerUtf8::Shared(
+ OwningRef::new(
+ Arc::new( String::from( "same" ) ) )
+ .map(|v| &**v)
+ ) );
+ assert_eq!( a, b );
+ }
+
+ #[test]
+ fn input_neq() {
+ let a = Input( InnerUtf8::Owned( "not same".into() ) );
+ let b = Input( InnerUtf8::Shared(
+ OwningRef::new(
+ Arc::new( String::from( "not at all same" ) ) )
+ .map(|v| &**v)
+ ) );
+ assert_ne!( a, b );
+ }
+
+
+
+} \ No newline at end of file
diff --git a/headers/src/data/mod.rs b/headers/src/data/mod.rs
new file mode 100644
index 0000000..4d22f1b
--- /dev/null
+++ b/headers/src/data/mod.rs
@@ -0,0 +1,23 @@
+//! A number of little helper types, which contain text.
+//!
+//! They provide mainly following functionality:
+//!
+//! 1. remember if the data is Ascii/Utf8
+//! - this might be extended at some point
+//! to contain non ascii data
+//! 2. make sure the types are cheap to clone, by
+//! sharing the text internally.
+//! - this is mainly helpful when parsing a mail
+//!
+//! Both main points are for features which I decided to
+//! to not yet implement, as such **there is a chance
+//! that this module will be removed int the future**.
+//!
+mod inner_item;
+pub use self::inner_item::*;
+
+mod input;
+pub use self::input::*;
+
+mod simple_item;
+pub use self::simple_item::*; \ No newline at end of file
diff --git a/headers/src/data/simple_item.rs b/headers/src/data/simple_item.rs
new file mode 100644
index 0000000..e0cd09f
--- /dev/null
+++ b/headers/src/data/simple_item.rs
@@ -0,0 +1,113 @@
+use std::ops::Deref;
+
+use soft_ascii_string::{ SoftAsciiStr, SoftAsciiString};
+
+use super::input::Input;
+use super::inner_item::{ InnerAscii, InnerUtf8 };
+
+#[cfg(feature="serde")]
+use serde::{Serialize, Deserialize};
+
+#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
+pub enum SimpleItem {
+ /// specifies that the Item is valid Ascii, nothing more
+ Ascii( InnerAscii ),
+ /// specifies that the Item is valid Utf8, nothing more
+ Utf8( InnerUtf8 )
+}
+
+impl SimpleItem {
+
+ pub fn as_str( &self ) -> &str {
+ use self::SimpleItem::*;
+ match *self {
+ Ascii( ref value ) => value.as_str(),
+ Utf8( ref value ) => value.as_str()
+ }
+ }
+
+ pub fn is_ascii( &self ) -> bool {
+ use self::SimpleItem::*;
+ match *self {
+ Ascii( .. ) => true,
+ Utf8( .. ) => false
+ }
+ }
+
+ pub fn from_utf8_input( s: Input ) -> Self {
+ SimpleItem::Utf8( s.0 )
+ }
+
+ pub fn from_utf8( s: String ) -> Self {
+ SimpleItem::Utf8( InnerUtf8::Owned( s ) )
+ }
+
+
+}
+
+impl Deref for SimpleItem {
+ type Target = str;
+
+ fn deref( &self ) -> &str {
+ use self::SimpleItem::*;
+ match *self {
+ Ascii( ref astr ) => astr.as_str(),
+ Utf8( ref utf8 ) => &**utf8
+ }
+ }
+}
+
+
+impl Into<String> for SimpleItem {
+ fn into(self) -> String {
+ use self::SimpleItem::*;
+ match self {
+ Ascii( aitem ) => {
+ let astring: SoftAsciiString = aitem.into();
+ astring.into()
+ },
+ Utf8( string ) => string.into()
+ }
+ }
+}
+
+impl<'a> From<&'a str> for SimpleItem {
+ fn from( string: &'a str ) -> Self {
+ Self::from( String::from( string ) )
+ }
+}
+
+impl From<String> for SimpleItem {
+ fn from( string: String ) -> Self {
+ match SoftAsciiString::from_string( string ) {
+ Ok( astring ) => SimpleItem::Ascii( InnerAscii::Owned( astring ) ),
+ Err( err ) => SimpleItem::Utf8( InnerUtf8::Owned( err.into_source() ) )
+ }
+ }
+}
+
+impl From<SoftAsciiString> for SimpleItem {
+ fn from( astring: SoftAsciiString ) -> Self {
+ SimpleItem::Ascii( InnerAscii::Owned( astring ) )
+ }
+}
+
+impl From<Input> for SimpleItem {
+ fn from(input: Input) -> Self {
+ match input {
+ Input( InnerUtf8::Owned( string ) ) => match SoftAsciiString::from_string( string ) {
+ Ok( ascii ) => SimpleItem::Ascii( InnerAscii::Owned( ascii ) ),
+ Err( err ) => SimpleItem::Utf8( InnerUtf8::Owned( err.into_source() ) )
+ },
+ Input( InnerUtf8::Shared( shared ) ) => {
+ if shared.is_ascii() {
+ let a_shared = shared.map(|s| SoftAsciiStr::from_unchecked(s));
+ SimpleItem::Ascii(InnerAscii::Shared(a_shared))
+ } else {
+ SimpleItem::Utf8(InnerUtf8::Shared(shared))
+ }
+ }
+ }
+ }
+}
diff --git a/headers/src/error.rs b/headers/src/error.rs
new file mode 100644
index 0000000..beb92cb
--- /dev/null
+++ b/headers/src/error.rs
@@ -0,0 +1,205 @@
+//! module contains the (new) errors emitted by this crate
+use std::fmt::{self, Display};
+
+use failure::{Fail, Context, Error as FError, Backtrace};
+
+use ::name::HeaderName;
+
+/// This error can occur if different implementations for the
+/// same header (e.g. `Subject`) where used in the same `HeaderMap`.
+#[derive(Debug, Fail)]
+#[fail(display = "cast error caused by mixing different header implementations for {}", header_name)]
+pub struct HeaderTypeError {
+ header_name: HeaderName,
+ backtrace: Backtrace
+}
+
+impl HeaderTypeError {
+ pub fn new(name: HeaderName) -> Self {
+ HeaderTypeError {
+ header_name: name,
+ backtrace: Backtrace::new()
+ }
+ }
+
+ pub fn new_with_backtrace(name: HeaderName, backtrace: Backtrace) -> Self {
+ HeaderTypeError {
+ header_name: name,
+ backtrace
+ }
+ }
+}
+
+/// A validator specified in a header definition failed.
+///
+/// Common validators are e.g. to make sure that if a
+/// From header has multiple mailboxes that there is
+/// a Sender header etc.
+#[derive(Debug, Fail)]
+pub enum HeaderValidationError {
+ #[fail(display = "{}", _0)]
+ BuildIn(Context<BuildInValidationError>),
+ #[fail(display = "{}", _0)]
+ Custom(FError)
+}
+
+impl From<BuildInValidationError> for HeaderValidationError {
+ fn from(err: BuildInValidationError) -> Self {
+ HeaderValidationError::BuildIn(Context::new(err))
+ }
+}
+
+impl From<Context<BuildInValidationError>> for HeaderValidationError {
+ fn from(err: Context<BuildInValidationError>) -> Self {
+ HeaderValidationError::BuildIn(err)
+ }
+}
+
+/// The build-in error variants (error kinds) which can be returned
+/// when running a header map validator.
+#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)]
+pub enum BuildInValidationError {
+
+ /// This error is returned by `use_contextual_validators` if there is a "max one" inconsistency.
+ ///
+ /// I.e. if multiple implementations of the same header are used in the same map but
+ /// the implementations do not agree on wether or not the header can appear at most one
+ /// time in a header section.
+ #[fail(display = "{} header field contained both \"multi\" and \"max one\" header impl", header_name)]
+ MaxOneInconsistency { header_name: &'static str },
+
+ #[fail(display = "{} header field can appear at most one time in a header map", header_name)]
+ MoreThenOne { header_name: &'static str },
+
+ #[fail(display = "From field contained multiple addresses but no Sender field was set")]
+ MultiMailboxFromWithoutSender,
+
+ #[fail(display = "each resent block must have a resent-date field")]
+ ResentDateFieldMissing,
+
+ #[fail(display = "Resent-From field in resent block without a Resent-Sender field")]
+ MultiMailboxResentFromWithoutResentSender
+}
+
+macro_rules! header_validation_bail {
+ (kind: $($tt:tt)*) => ({
+ let build_in = $crate::error::BuildInValidationError::$($tt)*;
+ return Err(HeaderValidationError::BuildIn(::failure::Context::new(build_in)));
+ });
+}
+
+
+/// Helper type which is either a `Backtrace` or an full `failure::Error`.
+///
+/// This can be used to either just contain a backtrace into an custom
+/// error or to chain it in front of another error without adding another
+/// backtrace, depending on the creating context.
+#[derive(Debug)]
+pub enum ChainTail {
+ Backtrace(Backtrace),
+ Error(FError)
+}
+
+impl ChainTail {
+
+ fn backtrace(&self) -> &Backtrace {
+ match *self {
+ ChainTail::Backtrace(ref trace) => trace,
+ ChainTail::Error(ref error) => error.backtrace()
+ }
+ }
+
+ fn as_fail(&self) -> Option<&Fail> {
+ match *self {
+ ChainTail::Backtrace(_) => None,
+ ChainTail::Error(ref error) => Some(error.as_fail())
+ }
+ }
+}
+
+/// Creating a (header field) component from the given data failed
+///
+/// A good example converting a string to a mailbox by parsing it,
+/// or more concretely failing to do so because it's not a valid
+/// mail address.
+#[derive(Debug)]
+pub struct ComponentCreationError {
+ component: &'static str,
+ backtrace: ChainTail,
+ str_context: Option<String>
+}
+
+impl ComponentCreationError {
+
+ /// create a new `ComponentCreationError` based on a different error and the name of the component
+ ///
+ /// The name is normally the type name, for example `Email`, `Mailbox` etc.
+ pub fn from_parent<P>(parent: P, component: &'static str) -> Self
+ where P: Into<FError>
+ {
+ ComponentCreationError {
+ component,
+ backtrace: ChainTail::Error(parent.into()),
+ str_context: None
+ }
+ }
+
+ /// creates a new `ComponentCreationError` based on the components name
+ ///
+ /// The name is normally the type name, for example `Email`, `Mailbox` etc.
+ pub fn new(component: &'static str) -> Self {
+ ComponentCreationError {
+ component,
+ backtrace: ChainTail::Backtrace(Backtrace::new()),
+ str_context: None
+ }
+ }
+
+ /// creates a new `ComponentCreationError` based on the components name with a str_context
+ ///
+ /// The name is normally the type name, for example `Email`, `Mailbox` etc.
+ ///
+ /// The `str_context` is a snipped of text which can help a human to identify the
+ /// invalid parts, e.g. for parsing a email it could be the invalid email address.
+ pub fn new_with_str<I>(component: &'static str, str_context: I) -> Self
+ where I: Into<String>
+ {
+ ComponentCreationError {
+ component,
+ backtrace: ChainTail::Backtrace(Backtrace::new()),
+ str_context: Some(str_context.into())
+ }
+ }
+
+ pub fn str_context(&self) -> Option<&str> {
+ self.str_context.as_ref().map(|s|&**s)
+ }
+
+ pub fn set_str_context<I>(&mut self, ctx: I)
+ where I: Into<String>
+ {
+ self.str_context = Some(ctx.into());
+ }
+
+ pub fn with_str_context<I>(mut self, ctx: I) -> Self
+ where I: Into<String>
+ {
+ self.set_str_context(ctx);
+ self
+ }
+}
+
+impl Fail for ComponentCreationError {
+ fn cause(&self) -> Option<&Fail> {
+ self.backtrace.as_fail()
+ }
+ fn backtrace(&self) -> Option<&Backtrace> {
+ Some(self.backtrace.backtrace())
+ }
+}
+
+impl Display for ComponentCreationError {
+ fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
+ write!(fter, "creating component {} failed", self.component)
+ }
+}
diff --git a/headers/src/header.rs b/headers/src/header.rs
new file mode 100644
index 0000000..ae63632
--- /dev/null
+++ b/headers/src/header.rs
@@ -0,0 +1,253 @@
+use std::any::TypeId;
+use std::ops::{Deref, DerefMut};
+use std::fmt::{self, Debug};
+
+use internals::{
+ error::EncodingError,
+ encoder::{
+ EncodableInHeader,
+ EncodingWriter,
+ }
+};
+
+
+use ::error::ComponentCreationError;
+use ::convert::HeaderTryInto;
+use ::name::{HeaderName, HasHeaderName};
+//NOTE: this is a circular dependency between Header/HeaderMap
+// but putting up e.g. a GenericHeaderMap trait/interface is
+// not worth the work at all
+use ::map::HeaderMapValidator;