diff options
Diffstat (limited to 'headers/src/header_components/unstructured.rs')
-rw-r--r-- | headers/src/header_components/unstructured.rs | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/headers/src/header_components/unstructured.rs b/headers/src/header_components/unstructured.rs new file mode 100644 index 0000000..84a4912 --- /dev/null +++ b/headers/src/header_components/unstructured.rs @@ -0,0 +1,194 @@ +use std::ops::{ Deref, DerefMut}; +use std::fmt::{self, Display}; + +use failure::Fail; +use soft_ascii_string::SoftAsciiChar; + +use internals::grammar::is_vchar; +use internals::error::{EncodingError, EncodingErrorKind}; +use internals::encoder::{EncodingWriter, EncodableInHeader}; +use internals::bind::encoded_word::{EncodedWordEncoding, WriterWrapper}; +use ::{HeaderTryFrom, HeaderTryInto}; +use ::error::ComponentCreationError; +use ::data::Input; + +use super::utils::text_partition::{partition, Partition}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Unstructured { + //FEATUR_TODO(non_utf8_input): split into parts each possibke having their own encoding + text: Input, +} + +impl Display for Unstructured { + fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result { + fter.write_str(self.as_str()) + } +} + +impl Deref for Unstructured { + type Target = Input; + + fn deref(&self) -> &Self::Target { + &self.text + } +} + +impl DerefMut for Unstructured { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.text + } +} + +impl<T> HeaderTryFrom<T> for Unstructured + where T: HeaderTryInto<Input> +{ + fn try_from(text: T) -> Result<Self, ComponentCreationError> { + let text = text.try_into()?; + Ok( Unstructured { text }) + } +} + + + +impl EncodableInHeader for Unstructured { + + fn encode(&self, handle: &mut EncodingWriter) -> Result<(), EncodingError> { + let text: &str = &*self.text; + if text.len() == 0 { + return Ok( () ) + } + + let partitions = partition(text) + .map_err(|err| EncodingError + ::from(err.context(EncodingErrorKind::Malformed)) + .with_str_context(text) + )?; + + for block in partitions.into_iter() { + match block { + Partition::VCHAR( data ) => { + let mail_type = handle.mail_type(); + handle.write_if(data, |s| + s.chars().all(|ch| is_vchar(ch, mail_type)) + ).handle_condition_failure(|handle| { + let encoding = EncodedWordEncoding::QuotedPrintable; + let mut writer = WriterWrapper::new( + encoding, + handle + ); + encoding.encode(data, &mut writer); + Ok(()) + })?; + }, + Partition::SPACE( data ) => { + let mut had_fws = false; + for ch in data.chars() { + if ch == '\r' || ch == '\n' { + continue; + } else if !had_fws { + handle.mark_fws_pos(); + had_fws = true; + } + handle.write_char( SoftAsciiChar::from_unchecked(ch) )?; + } + if !had_fws { + // currently this can only happen if data only consists of '\r','\n' + // which we strip which in turn would remove the spacing completely + //NOTE: space has to be at last one horizontal-white-space + // (required by the possibility of VCHAR partitions being + // encoded words) + handle.write_fws(); + } + } + } + + } + Ok(()) + } + + fn boxed_clone(&self) -> Box<EncodableInHeader> { + Box::new(self.clone()) + } +} + + +#[cfg(test)] +mod test { + + use super::*; + + ec_test! { simple_encoding, { + Unstructured::try_from( "this simple case" )? + } => ascii => [ + Text "this", + MarkFWS, + Text " simple", + MarkFWS, + Text " case" + ]} + + ec_test!{ simple_utf8, { + Unstructured::try_from( "thüs sümple case" )? + } => utf8 => [ + Text "thüs", + MarkFWS, + Text " sümple", + MarkFWS, + Text " case" + ]} + + ec_test!{ encoded_words, { + Unstructured::try_from( "↑ ↓ ←→ bA" )? + } => ascii => [ + Text "=?utf8?Q?=E2=86=91?=", + MarkFWS, + Text " =?utf8?Q?=E2=86=93?=", + MarkFWS, + Text " =?utf8?Q?=E2=86=90=E2=86=92?=", + MarkFWS, + Text " bA" + ]} + + ec_test!{ eats_cr_lf, { + Unstructured::try_from( "a \rb\n c\r\n " )? + } => ascii => [ + Text "a", + MarkFWS, + Text " b", + MarkFWS, + Text " c", + MarkFWS, + Text " " + ]} + + ec_test!{ at_last_one_fws, { + Unstructured::try_from( "a\rb\nc\r\n" )? + } => ascii => [ + Text "a", + MarkFWS, + Text " b", + MarkFWS, + Text " c", + MarkFWS, + Text " " + ]} + + ec_test!{ kinda_keeps_wsp, { + Unstructured::try_from("\t\ta b \t")? + } => ascii => [ + MarkFWS, + Text "\t\ta", + MarkFWS, + Text " b", + MarkFWS, + Text " \t" + ]} + + ec_test!{ wsp_only_phrase, { + Unstructured::try_from( " \t " )? + } => ascii => [ + MarkFWS, + Text " \t " + ]} +}
\ No newline at end of file |