mail_headers_ng/header_components/
unstructured.rs1use std::ops::{ Deref, DerefMut};
2use std::fmt::{self, Display};
3
4use failure::Fail;
5use soft_ascii_string::SoftAsciiChar;
6
7use internals::grammar::is_vchar;
8use internals::error::{EncodingError, EncodingErrorKind};
9use internals::encoder::{EncodingWriter, EncodableInHeader};
10use internals::bind::encoded_word::{EncodedWordEncoding, WriterWrapper};
11use ::{HeaderTryFrom, HeaderTryInto};
12use ::error::ComponentCreationError;
13use ::data::Input;
14
15use super::utils::text_partition::{partition, Partition};
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Unstructured {
19 text: Input,
21}
22
23impl Display for Unstructured {
24 fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
25 fter.write_str(self.as_str())
26 }
27}
28
29impl Deref for Unstructured {
30 type Target = Input;
31
32 fn deref(&self) -> &Self::Target {
33 &self.text
34 }
35}
36
37impl DerefMut for Unstructured {
38 fn deref_mut(&mut self) -> &mut Self::Target {
39 &mut self.text
40 }
41}
42
43impl<T> HeaderTryFrom<T> for Unstructured
44 where T: HeaderTryInto<Input>
45{
46 fn try_from(text: T) -> Result<Self, ComponentCreationError> {
47 let text = text.try_into()?;
48 Ok( Unstructured { text })
49 }
50}
51
52
53
54impl EncodableInHeader for Unstructured {
55
56 fn encode(&self, handle: &mut EncodingWriter) -> Result<(), EncodingError> {
57 let text: &str = &*self.text;
58 if text.len() == 0 {
59 return Ok( () )
60 }
61
62 let partitions = partition(text)
63 .map_err(|err| EncodingError
64 ::from(err.context(EncodingErrorKind::Malformed))
65 .with_str_context(text)
66 )?;
67
68 for block in partitions.into_iter() {
69 match block {
70 Partition::VCHAR( data ) => {
71 let mail_type = handle.mail_type();
72 handle.write_if(data, |s|
73 s.chars().all(|ch| is_vchar(ch, mail_type))
74 ).handle_condition_failure(|handle| {
75 let encoding = EncodedWordEncoding::QuotedPrintable;
76 let mut writer = WriterWrapper::new(
77 encoding,
78 handle
79 );
80 encoding.encode(data, &mut writer);
81 Ok(())
82 })?;
83 },
84 Partition::SPACE( data ) => {
85 let mut had_fws = false;
86 for ch in data.chars() {
87 if ch == '\r' || ch == '\n' {
88 continue;
89 } else if !had_fws {
90 handle.mark_fws_pos();
91 had_fws = true;
92 }
93 handle.write_char( SoftAsciiChar::from_unchecked(ch) )?;
94 }
95 if !had_fws {
96 handle.write_fws();
102 }
103 }
104 }
105
106 }
107 Ok(())
108 }
109
110 fn boxed_clone(&self) -> Box<EncodableInHeader> {
111 Box::new(self.clone())
112 }
113}
114
115
116#[cfg(test)]
117mod test {
118
119 use super::*;
120
121 ec_test! { simple_encoding, {
122 Unstructured::try_from( "this simple case" )?
123 } => ascii => [
124 Text "this",
125 MarkFWS,
126 Text " simple",
127 MarkFWS,
128 Text " case"
129 ]}
130
131 ec_test!{ simple_utf8, {
132 Unstructured::try_from( "thüs sümple case" )?
133 } => utf8 => [
134 Text "thüs",
135 MarkFWS,
136 Text " sümple",
137 MarkFWS,
138 Text " case"
139 ]}
140
141 ec_test!{ encoded_words, {
142 Unstructured::try_from( "↑ ↓ ←→ bA" )?
143 } => ascii => [
144 Text "=?utf8?Q?=E2=86=91?=",
145 MarkFWS,
146 Text " =?utf8?Q?=E2=86=93?=",
147 MarkFWS,
148 Text " =?utf8?Q?=E2=86=90=E2=86=92?=",
149 MarkFWS,
150 Text " bA"
151 ]}
152
153 ec_test!{ eats_cr_lf, {
154 Unstructured::try_from( "a \rb\n c\r\n " )?
155 } => ascii => [
156 Text "a",
157 MarkFWS,
158 Text " b",
159 MarkFWS,
160 Text " c",
161 MarkFWS,
162 Text " "
163 ]}
164
165 ec_test!{ at_last_one_fws, {
166 Unstructured::try_from( "a\rb\nc\r\n" )?
167 } => ascii => [
168 Text "a",
169 MarkFWS,
170 Text " b",
171 MarkFWS,
172 Text " c",
173 MarkFWS,
174 Text " "
175 ]}
176
177 ec_test!{ kinda_keeps_wsp, {
178 Unstructured::try_from("\t\ta b \t")?
179 } => ascii => [
180 MarkFWS,
181 Text "\t\ta",
182 MarkFWS,
183 Text " b",
184 MarkFWS,
185 Text " \t"
186 ]}
187
188 ec_test!{ wsp_only_phrase, {
189 Unstructured::try_from( " \t " )?
190 } => ascii => [
191 MarkFWS,
192 Text " \t "
193 ]}
194
195 ec_test!{ long_mixed_input, {
196 Unstructured::try_from("Subject: …. AAAAAAAAAAAAAAAAAAA….. AA…")?
197 } => ascii => [
198 Text "Subject:",
199 MarkFWS,
200 Text " =?utf8?Q?=E2=80=A6=2E?=",
201 MarkFWS,
202 Text " =?utf8?Q?AAAAAAAAAAAAAAAAAAA=E2=80=A6=2E=2E?=",
203 MarkFWS,
204 Text " =?utf8?Q?AA=E2=80=A6?="
205 ]}
206}