summaryrefslogtreecommitdiffstats
path: root/headers
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
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')
-rw-r--r--headers/Cargo.toml35
-rw-r--r--headers/README.md150
-rw-r--r--headers/doc/list_of_headers.txt78
-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
-rwxr-xr-xheaders/test.sh8
38 files changed, 6827 insertions, 0 deletions
diff --git a/headers/Cargo.toml b/headers/Cargo.toml
new file mode 100644
index 0000000..539ec5e
--- /dev/null
+++ b/headers/Cargo.toml
@@ -0,0 +1,35 @@
+[package]
+name = "mail-headers"
+description = "[internal/mail-api] header parts for the mail-api crate (inkl. header map and standard header impl)"
+version = "0.6.0-wip"
+authors = ["Philipp Korber <philippkorber@gmail.com>"]
+documentation = "https://docs.rs/mail-headers"
+keywords = []
+categories = []
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/1aim/mail-headers"
+
+[features]
+serde-impl = [ "serde", "vec1/serde" ]
+traceing = [ "mail-internals/traceing" ]
+
+[dependencies]
+failure = "0.1"
+owning_ref = "0.4"
+nom = "3.1.0"
+soft-ascii-string = "1"
+quoted-string = "0.6"
+mail-internals = { git="https://github.com/1aim/mail-internal" }
+vec1 = "1"
+chrono = "0.4"
+total-order-multi-map = "0.4.5"
+serde = { version="1.0", optional=true, features=["derive"] }
+
+[dependencies.mime]
+git="https://github.com/1aim/mime"
+branch="parser_revamp"
+features=["expose-param-utils"]
+version="0.4.0"
+
+[dev-dependencies]
+serde_test = "1.0.80"
diff --git a/headers/README.md b/headers/README.md
new file mode 100644
index 0000000..8a49553
--- /dev/null
+++ b/headers/README.md
@@ -0,0 +1,150 @@
+
+# mail-headers
+
+**Provides header specific functionality for the `mail` crate**
+
+---
+
+This crate provides header specific functionality for the `mail`
+crate. This includes:
+
+- `HeaderName`, `Header` and `HeaderMap` as for the general API
+- `HeaderTryFrom`, `HeaderTryInto` as the `TryFrom`/`TryInto`
+ traits are not stable but we need something similar to their
+ functionality.
+- a number of headers like `_To`,`_From`, `Sender`, `Subject`
+ and many more (Note that `_To` and `_From` are prefixed with
+ and `_` to prevent name collisions when importing them, i.e.
+ importing `_From as From` would shadow `std::convert::From`
+ which can lead to extremely irritating errors).
+- a number of components which are used to represent the
+ content/field body of an header field e.g. `MailboxList`
+ or `Email`. They are placed in the `components` module.
+- a `headers!` macro for making the creation of an `HeaderMap`
+ with a number of headers easier.
+- a `def_headers!` macro for defining new custom headers
+
+## Example (HeaderMap)
+
+A header map is a collection representing a number
+of mail headers in an specific order. It can be
+created like this:
+
+```rust
+#[macro_use]
+extern crate mail_headers;
+
+// just import all headers
+use mail_headers::*;
+use mail_headers::error::ComponentCreationError;
+
+fn create_headers() -> Result<HeaderMap, ComponentCreationError> {
+ headers!{
+ // from and to can have multiple values
+ // until specialization is stable is array
+ // is necessary
+ _From: [("My Fancy Display Name", "theduck@example.com")],
+ _To: [ "unknown@example.com", ],
+ Subject: "Who are you?"
+ }
+}
+
+fn main() {
+ let headers = create_headers().unwrap();
+ assert_eq!(headers.len(), 3);
+
+ if let Some(subject) = headers.get_single(Subject) {
+ // as long a you don't play around with custom headers AND
+ // don't mix multiple implementations for the same header
+ // `.unwrap()` is just fine.
+ let subject = subject.expect("mixed different Subject header implementations");
+ println!("found subject: {}", subject);
+ }
+}
+```
+
+## Example (custom header)
+
+If needed users of the `mail` crate can create their own
+headers, through this should be done with care.
+
+Note that the second field (then `unchecked { <name> }`),
+expects a specific naming scheme, the auto-generated test
+do check if it's violated but if you just run the code and
+ignore the failing tests strange error can occure. (
+The scheme is a capitalise the first letter of each
+word and write all other letter in lower case, i.e.
+`X-Id` is ok but `X-ID` isn't). The reason for this is because
+of the way the header does the field lookup. While this
+is not nice, for most use cases there is no need to
+generate custom headers and in the future this might be
+circumvented by auto-generating the name with a proc-derive.
+
+```rust
+#[macro_use]
+extern crate mail_headers;
+
+use mail_headers::components;
+
+// this will define two headers `XFooEmail` and `XBarMailbox`
+// the first will add a header field named `X-Foo-Email` with
+// a value which is an `components::Email` and the second will
+// add field with a value which is an `components::Mailbox`.
+//
+// Note that through the way they are defined `XFooEmail` can
+// at most appear 1 time in an header map, while `XBarMailbox`
+// can appear multiple times. Be aware that this is checked through
+// so called validators which needs to be explicitly run, which they
+// are if this header map is used to create a mail (running them
+// when adding fields would be a mess as you would have to add
+// transactions which can add/remove multiple fields at once, and
+// implementing auto-generation for some fields which are required if
+// some other fields are given in a certain way would be harder too).
+
+// If in scope both can be used in the `headers!` macro,
+// like any other header.
+//
+def_headers! {
+ // the name of the auto-generated test
+ test_name: validate_header_names,
+
+ // the scope from which all components should be imported
+ // E.g. `DateTime` refers to `components::DateTime`.
+ scope: components,
+
+ // definitions of the headers or the form
+ // <type_name>, unchecked { <filed_name> }, <component>, <validator>
+ XFooEmail, unchecked { "X-Foo-Email" }, Email , maxOne,
+ XBarMailbox, unchecked { "X-Bar-Mailbox" }, Mailbox, None
+}
+
+fn main() {
+ let headers = headers! {
+ XFooEmail: "123@example.com",
+ XBarMailbox: ("My Funy Name", "notfunny@example.com"),
+ XBarMailbox: "without.display.name@example.com"
+ }.unwrap();
+}
+```
+
+
+## Documentation
+
+Documentation can be [viewed on docs.rs](https://docs.rs/mail-headers).
+(once it is published ;) )
+
+
+## License
+
+Licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
+additional terms or conditions.
diff --git a/headers/doc/list_of_headers.txt b/headers/doc/list_of_headers.txt
new file mode 100644
index 0000000..7cf1666
--- /dev/null
+++ b/headers/doc/list_of_headers.txt
@@ -0,0 +1,78 @@
+-- FIXME AsciiString => HeaderName
+-- NOT HERE Content-Header-Extension |AsciiString | Unstructured|,
+-- NOT HERE Other |AsciiString | Unstructured|,
+
+RFC | Name | Rust-Type | Comment
+------|---------------------------|-------------------|----------------------------
+5322 | | | RFC 5322 obsoletes RFC 822
+ | Date | DateTime |
+ | From | MailboxList |
+ | Sender | Mailbox |
+ | Reply-To | MailboxList |
+ | To | MailboxList |
+ | Cc | MailboxList |
+ | Bcc | OptMailboxList |
+ | Message-ID | MessageID |
+ | In-Reply-To | MessageIDList |
+ | References | MessageIDList |
+ | Subject | Unstructured |
+ | Comments | Unstructured |
+ | Keywords | PhraseList |
+ | Resent-Date | DateTime |
+ | Resent-From | MailboxList |
+ | Resent-Sender | Mailbox |
+ | Resent-To | MailboxList |
+ | Resent-Cc | MailboxList |
+ | Resent-Bcc | OptMailboxList |
+ | Resent-Msg-ID | MessageID |
+ | Return-Path | Path |
+ | Received | ReceivedToken |
+------|---------------------------|-------------------|---------------------------
+2045 | Content-Type | Mime |
+ | Content-ID | MessageID |
+ | Content-Transfer-Encoding | TransferEncoding |
+ | Content-Description | Unstructured | the rfc states it is TEXT, but referes to RFC822
+ | | | in RFC5322 there is no longer TEXT, it was replaced
+ | | | by Unstructured
+------|---------------------------|-------------------|---------------------------
+2183 | | | proposed standard (obsoltets rfc 1806)
+ | Content-Disposition | Disposition |
+------|---------------------------|-------------------|---------------------------
+
+
+
+------ "others" ----
+-- e.g. see https://www.cs.tut.fi/~jkorpela/headers.html
+--Delivered-To |loop detection|
+--User-Agent |client software used by orginator|
+--Abuse-Reports-To |inserted by some servers|
+--X-Envelop-From |Mailbox| |sender in the envelop copied into the body|
+--X-Envelop-To |Mailbox| |again envelop information moved into body|
+--X-Remote-Addr |from html|
+--
+------Proposed Standard----
+--RFC 1766
+-- Content-Language |LanguageTag|
+--RFC 1864
+-- Content-MD5 |Base64|
+--
+------Experimental--------
+--RFC 1806 |attachment of inline|
+-- Content-Disposition |Dispositions|
+--RFC 1327 & 1911
+-- Importance
+-- Sensitivity
+--RFC 1154 & 1505
+-- Encoding
+--
+------Not Standad ------
+--RFC 1036
+-- FollowupTo |??MessageID|
+--RFC 1036 |count of lines|
+-- Lines |usize|
+--RFC ????
+-- Status |U/R/O/D/N| |should NEVER EVER be generate for a mail to send, use by some mail delivery systems INTERNAL ONLY|
+--
+--
+------Not Standard Discouraged----
+--ContentLength |usize| |do never generate content length header in a mail you send, it's a HTTP think| \ No newline at end of file
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, Deseria