mail_headers/header_components/
word.rs1use quoted_string;
2
3use internals::grammar::is_atext;
4use internals::grammar::encoded_word::EncodedWordContext;
5use internals::error::{EncodingError, EncodingErrorKind};
6use internals::encoder::{EncodingWriter, EncodableInHeader};
7use internals::bind::encoded_word::{EncodedWordEncoding, WriterWrapper};
8use internals::bind::quoted_string::{MailQsSpec, InternationalizedMailQsSpec};
9use ::{HeaderTryFrom, HeaderTryInto};
10use ::error::ComponentCreationError;
11use ::data::Input;
12
13
14use super::CFWS;
15
16
17#[derive( Debug, Clone, Eq, PartialEq, Hash )]
21pub struct Word {
22 pub left_padding: Option<CFWS>,
23 pub input: Input,
24 pub right_padding: Option<CFWS>
25}
26
27impl<T> HeaderTryFrom<T> for Word
28 where T: HeaderTryInto<Input>
29{
30
31 fn try_from( input: T ) -> Result<Self, ComponentCreationError> {
32 let input = input.try_into()?;
36 Ok( Word { left_padding: None, input, right_padding: None } )
40 }
41}
42
43impl Word {
44
45 pub fn pad_left( &mut self, padding: CFWS) {
46 self.left_padding = Some( padding )
47 }
48
49 pub fn pad_right( &mut self, padding: CFWS) {
50 self.right_padding = Some( padding )
51 }
52
53}
54
55
56pub fn do_encode_word<'a,'b: 'a>(
67 word: &'a Word,
68 handle: &'a mut EncodingWriter<'b>,
69 ecw_ctx: Option<EncodedWordContext>,
70) -> Result<(), EncodingError> {
71
72 if let Some( pad ) = word.left_padding.as_ref() {
73 pad.encode( handle )?;
74 }
75
76 let input: &str = &*word.input;
77 let mail_type = handle.mail_type();
78 handle.write_if(input, |input| {
79 (!input.contains("=?"))
80 && input.chars().all( |ch| is_atext( ch, mail_type ) )
81
82 }).handle_condition_failure(|handle| {
83 if let Some( _ecw_ctx ) = ecw_ctx {
84 let encoding = EncodedWordEncoding::QuotedPrintable;
86 let mut writer = WriterWrapper::new(
87 encoding,
88 handle
89 );
90 encoding.encode(input, &mut writer);
91 Ok(())
92 } else {
93 let mail_type = handle.mail_type();
94 let res =
95 if mail_type.is_internationalized() {
96 quoted_string::quote::<InternationalizedMailQsSpec>(input)
98 } else {
99 quoted_string::quote::<MailQsSpec>(input)
101 };
102 let quoted = res.map_err(|_err| {
103 EncodingError
104 ::from(EncodingErrorKind::Malformed)
105 .with_str_context(input)
106 })?;
107 handle.write_str_unchecked(&*quoted)
108 }
109 })?;
110
111 if let Some( pad ) = word.right_padding.as_ref() {
112 pad.encode( handle )?;
113 }
114 Ok( () )
115}
116
117
118#[cfg(test)]
119mod test {
120 use std::mem;
121
122 use internals::MailType;
123 use internals::encoder::EncodingBuffer;
124 use internals::encoder::TraceToken::*;
125 use internals::encoder::simplify_trace_tokens;
126
127 use super::*;
128 use super::super::FWS;
129
130
131 ec_test!{encode_pseudo_encoded_words, {
132 let word = Word::try_from( "=?" )?;
133 enc_closure!(move |handle: &mut EncodingWriter| {
134 do_encode_word( &word, handle, Some( EncodedWordContext::Text ) )
135 })
136 } => ascii => [
137 Text "=?utf8?Q?=3D=3F?="
138 ]}
139
140 ec_test!{encode_word, {
141 let word = Word::try_from( "a↑b" )?;
142 enc_closure!(move |handle: &mut EncodingWriter| {
143 do_encode_word( &word, handle, Some( EncodedWordContext::Text ) )
144 })
145 } => ascii => [
146 Text "=?utf8?Q?a=E2=86=91b?="
147 ]}
148
149
150 #[test]
151 fn encode_fails() {
152 let mut encoder = EncodingBuffer::new(MailType::Ascii);
153 let mut handle = encoder.writer();
154 let word = Word::try_from( "a↑b" ).unwrap();
155 assert_err!(do_encode_word( &word, &mut handle, None ));
156 handle.undo_header();
157 }
158
159
160 ec_test!{quoted_fallback, {
161 let word = Word::try_from( "a\"b" )?;
162 enc_closure!(move |handle: &mut EncodingWriter| {
163 do_encode_word( &word, handle, None )
164 })
165 } => ascii => [
166 Text r#""a\"b""#
167 ]}
168
169
170 #[test]
171 fn encode_word_padding() {
172 let words = &[
173 ( Word {
174 left_padding: None,
175 input: "abc".into(),
176 right_padding: None,
177 }, vec![
178 Text("abc".into())
179 ] ),
180 ( Word {
181 left_padding: Some( CFWS::SingleFws( FWS) ),
182 input: "abc".into(),
183 right_padding: None,
184 }, vec![
185 MarkFWS,
186 Text(" abc".into())
187 ] ),
188 ( Word {
189 left_padding: Some( CFWS::SingleFws( FWS ) ),
190 input: "abc".into(),
191 right_padding: Some( CFWS::SingleFws( FWS ) ),
192 }, vec![
193 MarkFWS,
194 Text(" abc".into()),
195 MarkFWS,
196 Text(" ".into())
197 ] ),
198 ( Word {
199 left_padding: None,
200 input: "abc".into(),
201 right_padding: Some( CFWS::SingleFws( FWS ) ),
202 }, vec![
203 Text("abc".into()),
204 MarkFWS,
205 Text(" ".into())
206 ] )
207 ];
208
209 for &( ref word, ref expection) in words.iter() {
210 let mut encoder = EncodingBuffer::new(MailType::Ascii);
211 {
212 let mut handle = encoder.writer();
213 do_encode_word( word, &mut handle, None ).unwrap();
214 mem::forget(handle);
215 }
216 assert_eq!(
217 &simplify_trace_tokens(encoder.trace.into_iter()),
218 expection
219 );
220 }
221 }
222
223
224}