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 {
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 {
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 "
]}
}