1use std::borrow::Cow;
18use std::str;
19
20use failure::Fail;
21use soft_ascii_string::{SoftAsciiStr, SoftAsciiChar};
22
23use grammar::is_atext;
24use ::utils::{
25 is_utf8_continuation_byte,
26 vec_insert_bytes
27};
28use ::MailType;
29use ::error::{
30 EncodingError, EncodingErrorKind,
31 UNKNOWN, UTF_8, US_ASCII
32};
33
34#[cfg(feature="traceing")]
35#[cfg_attr(test, macro_use)]
36mod trace;
37#[cfg_attr(test, macro_use)]
38mod encodable;
39
40
41#[cfg(feature="traceing")]
42pub use self::trace::*;
43pub use self::encodable::*;
44
45pub const LINE_LEN_SOFT_LIMIT: usize = 78;
47pub const LINE_LEN_HARD_LIMIT: usize = 998;
49
50pub const NEWLINE: &str = "\r\n";
51pub const NEWLINE_WITH_SPACE: &str = "\r\n ";
52
53
54pub struct EncodingBuffer {
56 mail_type: MailType,
57 buffer: Vec<u8>,
58 #[cfg(feature="traceing")]
59 pub trace: Vec<TraceToken>
60}
61
62impl EncodingBuffer {
63
64 pub fn new(mail_type: MailType) -> Self {
66 EncodingBuffer {
67 mail_type,
68 buffer: Vec::new(),
69 #[cfg(feature="traceing")]
70 trace: Vec::new()
71 }
72 }
73
74 pub fn mail_type( &self ) -> MailType {
76 self.mail_type
77 }
78
79 pub fn writer(&mut self) -> EncodingWriter {
83 #[cfg(not(feature="traceing"))]
84 {
85 EncodingWriter::new(self.mail_type, &mut self.buffer)
86 }
87 #[cfg(feature="traceing")]
88 {
89 EncodingWriter::new(self.mail_type, &mut self.buffer, &mut self.trace)
90 }
91 }
92
93 pub fn write_header_line<FN>(&mut self, func: FN) -> Result<(), EncodingError>
101 where FN: FnOnce(&mut EncodingWriter) -> Result<(), EncodingError>
102 {
103 let mut handle = self.writer();
104 match func(&mut handle) {
105 Ok(()) => {
106 handle.finish_header();
107 Ok(())
108 },
109 Err(e) => {
110 handle.undo_header();
111 Err(e)
112 }
113 }
114
115 }
116
117 pub fn write_blank_line(&mut self) {
118 self.buffer.extend(NEWLINE.as_bytes());
120 #[cfg(feature="traceing")]
121 { self.trace.push(TraceToken::BlankLine); }
122 }
123
124 pub fn write_body_unchecked(&mut self, body: &impl AsRef<[u8]>) {
126 let slice = body.as_ref();
127 self.buffer.extend(slice);
128 if !slice.ends_with(NEWLINE.as_bytes()) {
129 self.buffer.extend(NEWLINE.as_bytes());
130 }
131 }
132
133 pub fn as_str(&self) -> Result<&str, EncodingError> {
145 str::from_utf8(self.buffer.as_slice())
146 .map_err(|err| {
147 EncodingError::from((
148 err.context(EncodingErrorKind::InvalidTextEncoding {
149 expected_encoding: UTF_8,
150 got_encoding: UNKNOWN
151 }),
152 self.mail_type()
153 ))
154 })
155 }
156
157 pub fn to_string(&self) -> Result<String, EncodingError> {
159 Ok(self.as_str()?.to_owned())
160 }
161
162 pub fn to_string_lossy(&self) -> Cow<str> {
164 String::from_utf8_lossy(self.buffer.as_slice())
165 }
166
167 pub fn as_slice(&self) -> &[u8] {
169 &self.buffer
170 }
171
172}
173
174
175impl Into<Vec<u8>> for EncodingBuffer {
176 fn into(self) -> Vec<u8> {
177 self.buffer
178 }
179}
180
181impl Into<(MailType, Vec<u8>)> for EncodingBuffer {
182 fn into(self) -> (MailType, Vec<u8>) {
183 (self.mail_type, self.buffer)
184 }
185}
186
187#[cfg(feature="traceing")]
188impl Into<(MailType, Vec<u8>, Vec<TraceToken>)> for EncodingBuffer {
189 fn into(self) -> (MailType, Vec<u8>, Vec<TraceToken>) {
190 let EncodingBuffer { mail_type, buffer, trace } = self;
191 (mail_type, buffer, trace)
192 }
193}
194
195pub struct EncodingWriter<'a> {
213 buffer: &'a mut Vec<u8>,
214 #[cfg(feature="traceing")]
215 trace: &'a mut Vec<TraceToken>,
216 mail_type: MailType,
217 line_start_idx: usize,
218 last_fws_idx: usize,
219 skipped_cr: bool,
220 content_since_fws: bool,
224 content_before_fws: bool,
228 last_fws_has_char: bool,
230 header_start_idx: usize,
231 #[cfg(feature="traceing")]
232 trace_start_idx: usize
233}
234
235#[cfg(feature="traceing")]
236impl<'a> Drop for EncodingWriter<'a> {
237
238 fn drop(&mut self) {
239 use std::thread;
240 if !thread::panicking() && self.has_unfinished_parts() {
241 panic!("dropped Handle which partially wrote header to back buffer (use `finish_header` or `discard`)")
244 }
245 }
246}
247
248impl<'inner> EncodingWriter<'inner> {
249
250 #[cfg(not(feature="traceing"))]
251 fn new(
252 mail_type: MailType,
253 buffer: &'inner mut Vec<u8>,
254 ) -> Self {
255 let start_idx = buffer.len();
256 EncodingWriter {
257 buffer,
258 mail_type,
259 line_start_idx: start_idx,
260 last_fws_idx: start_idx,
261 skipped_cr: false,
262 content_since_fws: false,
263 content_before_fws: false,
264 header_start_idx: start_idx,
265 last_fws_has_char: false,
266 }
267 }
268
269 #[cfg(feature="traceing")]
270 fn new(
271 mail_type: MailType,
272 buffer: &'inner mut Vec<u8>,
273 trace: &'inner mut Vec<TraceToken>
274 ) -> Self {
275 let start_idx = buffer.len();
276 let trace_start_idx = trace.len();
277 EncodingWriter {
278 buffer,
279 trace,
280 mail_type,
281 line_start_idx: start_idx,
282 last_fws_idx: start_idx,
283 skipped_cr: false,
284 content_since_fws: false,
285 content_before_fws: false,
286 header_start_idx: start_idx,
287 last_fws_has_char: false,
288 trace_start_idx
289 }
290 }
291
292 fn reinit(&mut self) {
293 let start_idx = self.buffer.len();
294 self.line_start_idx = start_idx;
295 self.last_fws_idx = start_idx;
296 self.skipped_cr = false;
297 self.content_since_fws = false;
298 self.content_before_fws = false;
299 self.header_start_idx = start_idx;
300 #[cfg(feature="traceing")]
301 { self.trace_start_idx = self.trace.len(); }
302 }
303
304 #[inline]
306 pub fn has_unfinished_parts(&self) -> bool {
307 self.buffer.len() != self.header_start_idx
308 }
309
310 #[inline]
312 pub fn mail_type(&self) -> MailType {
313 self.mail_type
314 }
315
316 #[inline]
318 pub fn line_has_content(&self) -> bool {
319 self.content_before_fws | self.content_since_fws
320 }
321
322 #[inline]
324 pub fn current_line_byte_length(&self) -> usize {
325 self.buffer.len() - self.line_start_idx
326 }
327
328 pub fn mark_fws_pos(&mut self) {
334 #[cfg(feature="traceing")]
335 { self.trace.push(TraceToken::MarkFWS) }
336 self.content_before_fws |= self.content_since_fws;
337 self.content_since_fws = false;
338 self.last_fws_idx = self.buffer.len();
339 self.last_fws_has_char = false;
340 }
341
342 pub fn write_char(&mut self, ch: SoftAsciiChar) -> Result<(), EncodingError> {
352 #[cfg(feature="traceing")]
353 { self.trace.push(TraceToken::NowChar) }
354 let mut buffer = [0xff_u8; 4];
355 let ch: char = ch.into();
356 let slice = ch.encode_utf8(&mut buffer);
357 self.internal_write_char(slice)
358 }
359
360 pub fn write_str(&mut self, s: &SoftAsciiStr) -> Result<(), EncodingError> {
376 #[cfg(feature="traceing")]
377 { self.trace.push(TraceToken::NowStr) }
378 self.internal_write_str(s.as_str())
379 }
380
381
382 pub fn write_if_utf8<'short>(&'short mut self, s: &str)
398 -> ConditionalWriteResult<'short, 'inner>
399 {
400 if self.mail_type().is_internationalized() {
401 #[cfg(feature="traceing")]
402 { self.trace.push(TraceToken::NowUtf8) }
403 self.internal_write_str(s).into()
404 } else {
405 ConditionalWriteResult::ConditionFailure(self)
406 }
407 }
408
409 pub fn write_utf8(&mut self, s: &str) -> Result<(), EncodingError> {
410 if self.mail_type().is_internationalized() {
411 #[cfg(feature="traceing")]
412 { self.trace.push(TraceToken::NowUtf8) }
413 self.internal_write_str(s)
414 } else {
415 let mut err = EncodingError::from((
416 EncodingErrorKind::InvalidTextEncoding {
417 expected_encoding: US_ASCII,
418 got_encoding: UTF_8
419 },
420 self.mail_type()
421 ));
422 let raw_line = &self.buffer[self.line_start_idx..];
423 let mut line = String::from_utf8_lossy(raw_line).into_owned();
424 line.push_str(s);
425 err.set_str_context(line);
426 Err(err)
427 }
428 }
429
430 pub fn write_if_atext<'short>(&'short mut self, s: &str)
458 -> ConditionalWriteResult<'short, 'inner>
459 {
460 if s.chars().all( |ch| is_atext( ch, self.mail_type() ) ) {
461 #[cfg(feature="traceing")]
462 { self.trace.push(TraceToken::NowAText) }
463 self.internal_write_str(s).into()
465 } else {
466 ConditionalWriteResult::ConditionFailure(self)
467 }
468 }
469
470 pub fn write_if<'short, FN>(&'short mut self, s: &str, cond: FN)
475 -> ConditionalWriteResult<'short, 'inner>
476 where FN: FnOnce(&str) -> bool
477 {
478 if cond(s) {
479 #[cfg(feature="traceing")]
480 { self.trace.push(TraceToken::NowCondText) }
481 self.internal_write_str(s).into()
483 } else {
484 ConditionalWriteResult::ConditionFailure(self)
485 }
486 }
487
488 pub fn write_str_unchecked( &mut self, s: &str) -> Result<(), EncodingError> {
513 #[cfg(feature="traceing")]
514 { self.trace.push(TraceToken::NowUnchecked) }
515 self.internal_write_str(s)
516 }
517
518 pub fn commit_partial_header(&mut self) {
526 #[cfg(feature="traceing")]
527 { if let Some(&TraceToken::End) = self.trace.last() {}
528 else { self.trace.push(TraceToken::End) } }
529 self.reinit();
530 }
531
532 pub fn finish_header(&mut self) {
550 self.start_new_line();
551 #[cfg(feature="traceing")]
552 { if let Some(&TraceToken::End) = self.trace.last() {}
553 else { self.trace.push(TraceToken::End) } }
554 self.reinit();
555 }
556
557 pub fn undo_header(&mut self) {
567 self.buffer.truncate(self.header_start_idx);
568 #[cfg(feature="traceing")]
569 { self.trace.truncate(self.trace_start_idx); }
570 self.reinit();
571 }
572
573
574
575 pub fn write_fws(&mut self) {
591 self.mark_fws_pos();
592 self.last_fws_has_char = true;
593 let _ = self.write_char(SoftAsciiChar::from_unchecked(' '));
595 }
596
597
598
599 fn internal_write_str(&mut self, s: &str) -> Result<(), EncodingError> {
607 if s.is_empty() {
608 return Ok(());
609 }
610 let mut start = 0;
612 for (idx_m1, bch) in s.as_bytes()[1..].iter().enumerate() {
615 if !is_utf8_continuation_byte(*bch) {
616 let end = idx_m1 + 1;
618 self.internal_write_char(&s[start..end])?;
619 start = end;
620 }
621 }
622
623 self.internal_write_char(&s[start..])?;
625 Ok(())
626 }
627
628 fn start_new_line(&mut self) {
633 if self.line_has_content() {
634 #[cfg(feature="traceing")]
635 { self.trace.push(TraceToken::CRLF) }
636
637 self.buffer.push(b'\r');
638 self.buffer.push(b'\n');
639 } else {
640 #[cfg(feature="traceing")]
641 {
642 if self.buffer.len() > self.line_start_idx {
643 self.trace.push(TraceToken::TruncateToCRLF);
644 }
645 }
646 self.buffer.truncate(self.line_start_idx);
651 }
652 self.line_start_idx = self.buffer.len();
653 self.content_since_fws = false;
654 self.content_before_fws = false;
655 self.last_fws_idx = self.line_start_idx;
656
657 }
658
659 fn break_line_on_fws(&mut self) -> bool {
660 if self.content_before_fws && self.last_fws_idx > self.line_start_idx {
661 let newline =
662 if self.last_fws_has_char {
663 debug_assert!([b' ', b'\t'].contains(&self.buffer[self.last_fws_idx]));
664 NEWLINE
665 } else {
666 NEWLINE_WITH_SPACE
667 };
668
669 vec_insert_bytes(&mut self.buffer, self.last_fws_idx, newline.as_bytes());
670 self.line_start_idx = self.last_fws_idx + 2;
671 self.content_before_fws = false;
672 true
673 } else {
674 false
675 }
676 }
677
678 fn internal_write_char(&mut self, unchecked_utf8_char: &str) -> Result<(), EncodingError> {
694 debug_assert_eq!(unchecked_utf8_char.chars().count(), 1);
695
696 let bch = unchecked_utf8_char.as_bytes()[0];
697 if bch == b'\n' {
698 if self.skipped_cr {
699 self.start_new_line()
700 } else {
701 ec_bail!(
702 mail_type: self.mail_type(),
703 kind: Malformed
704 );
705 }
706 self.skipped_cr = false;
707 return Ok(());
708 } else {
709 if self.skipped_cr {
710 ec_bail!(
711 mail_type: self.mail_type(),
712 kind: Malformed
713 );
714 }
715 if bch == b'\r' {
716 self.skipped_cr = true;
717 return Ok(());
718 } else {
719 self.skipped_cr = false;
720 }
721 }
722
723 if self.current_line_byte_length() >= LINE_LEN_SOFT_LIMIT {
724 self.break_line_on_fws();
725
726 if self.current_line_byte_length() >= LINE_LEN_HARD_LIMIT {
727 ec_bail!(
728 mail_type: self.mail_type(),
729 kind: HardLineLengthLimitBreached
730 );
731 }
732 }
733
734 self.buffer.extend(unchecked_utf8_char.as_bytes());
735 #[cfg(feature="traceing")]
736 {
737 let need_new =
739 if let Some(&mut TraceToken::Text(ref mut string)) = self.trace.last_mut() {
740 string.push_str(unchecked_utf8_char);
741 false
742 } else {
743 true
744 };
745 if need_new {
746 let mut string = String::new();
747 string.push_str(unchecked_utf8_char);
748 self.trace.push(TraceToken::Text(string))
749 }
750
751 }
752
753 if bch != b' ' && bch != b'\t' {
755 self.content_since_fws = true;
758 }
759 Ok(())
760 }
761}
762
763pub enum ConditionalWriteResult<'a, 'b: 'a> {
764 Ok,
765 ConditionFailure(&'a mut EncodingWriter<'b>),
766 GeneralFailure(EncodingError)
767}
768
769impl<'a, 'b: 'a> From<Result<(), EncodingError>> for ConditionalWriteResult<'a, 'b> {
770 fn from(v: Result<(), EncodingError>) -> Self {
771 match v {
772 Ok(()) => ConditionalWriteResult::Ok,
773 Err(e) => ConditionalWriteResult::GeneralFailure(e)
774 }
775 }
776}
777
778impl<'a, 'b: 'a> ConditionalWriteResult<'a, 'b> {
779
780 #[inline]
781 pub fn handle_condition_failure<FN>(self, func: FN) -> Result<(), EncodingError>
782 where FN: FnOnce(&mut EncodingWriter) -> Result<(), EncodingError>
783 {
784 use self::ConditionalWriteResult as CWR;
785
786 match self {
787 CWR::Ok => Ok(()),
788 CWR::ConditionFailure(handle) => {
789 func(handle)
790 },
791 CWR::GeneralFailure(err) => Err(err)
792 }
793 }
794}
795
796
797
798
799
800#[cfg(test)]
801mod test {
802
803 use soft_ascii_string::{ SoftAsciiChar, SoftAsciiStr};
804 use ::MailType;
805 use ::error::EncodingErrorKind;
806
807 use super::TraceToken::*;
808 use super::{EncodingBuffer as _Encoder};
809
810 mod test_test_utilities {
811 use encoder::TraceToken::*;
812 use super::super::simplify_trace_tokens;
813
814 #[test]
815 fn does_simplify_tokens_strip_nows() {
816 let inp = vec![
817 NowChar,
818 Text("h".into()),
819 CRLF,
820 NowStr,
821 Text("y yo".into()),
822 CRLF,
823 NowUtf8,
824 Text(", what's".into()),
825 CRLF,
826 NowUnchecked,
827 Text("up!".into()),
828 CRLF,
829 NowAText,
830 Text("abc".into())
831 ];
832 let out = simplify_trace_tokens(inp);
833 assert_eq!(out, vec![
834 Text("h".into()),
835 CRLF,
836 Text("y yo".into()),
837 CRLF,
838 Text(", what's".into()),
839 CRLF,
840 Text("up!".into()),
841 CRLF,
842 Text("abc".into())
843 ])
844
845 }
846
847 #[test]
848 fn simplify_does_collapse_text() {
849 let inp = vec![
850 NowChar,
851 Text("h".into()),
852 NowStr,
853 Text("y yo".into()),
854 NowUtf8,
855 Text(", what's".into()),
856 NowUnchecked,
857 Text(" up! ".into()),
858 NowAText,
859 Text("abc".into())
860 ];
861 let out = simplify_trace_tokens(inp);
862 assert_eq!(out, vec![
863 Text("hy yo, what's up! abc".into())
864 ]);
865 }
866
867 #[test]
868 fn simplify_works_with_empty_text() {
869 let inp = vec![
870 NowStr,
871 Text("".into()),
872 CRLF,
873 ];
874 assert_eq!(simplify_trace_tokens(inp), vec![
875 Text("".into()),
876 CRLF
877 ])
878 }
879
880 #[test]
881 fn simplify_works_with_trailing_empty_text() {
882 let inp = vec![
883 Text("a".into()),
884 CRLF,
885 Text("".into()),
886 ];
887 assert_eq!(simplify_trace_tokens(inp), vec![
888 Text("a".into()),
889 CRLF,
890 Text("".into())
891 ])
892 }
893
894 }
895
896 mod EncodableInHeader {
897 #![allow(non_snake_case)]
898 use super::super::*;
899 use self::TraceToken::*;
900
901 #[test]
902 fn is_implemented_for_closures() {
903 let closure = enc_func!(|handle: &mut EncodingWriter| {
904 handle.write_utf8("hy ho")
905 });
906
907 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
908 {
909 let mut handle = encoder.writer();
910 assert_ok!(closure.encode(&mut handle));
911 handle.finish_header();
912 }
913 assert_eq!(encoder.trace.as_slice(), &[
914 NowUtf8,
915 Text("hy ho".into()),
916 CRLF,
917 End
918 ])
919 }
920 }
921
922
923 mod EncodingBuffer {
924 #![allow(non_snake_case)]
925 use super::*;
926 use super::{ _Encoder as EncodingBuffer };
927
928 #[test]
929 fn new_encoder() {
930 let encoder = EncodingBuffer::new(MailType::Internationalized);
931 assert_eq!(encoder.mail_type(), MailType::Internationalized);
932 }
933
934 #[test]
935 fn write_body_unchecked() {
936 let mut encoder = EncodingBuffer::new(MailType::Ascii);
937 let body1 = "una body\r\n";
938 let body2 = "another body";
939
940 encoder.write_body_unchecked(&body1);
941 encoder.write_blank_line();
942 encoder.write_body_unchecked(&body2);
943
944 assert_eq!(
945 encoder.as_slice(),
946 concat!(
947 "una body\r\n",
948 "\r\n",
949 "another body\r\n"
950 ).as_bytes()
951 )
952 }
953 }
954
955
956 mod EncodingWriter {
957 #![allow(non_snake_case)]
958 use std::mem;
959 use std::str;
960
961 use super::*;
962 use super::{ _Encoder as EncodingBuffer };
963
964 #[test]
965 fn commit_partial_and_drop_does_not_panic() {
966 let mut encoder = EncodingBuffer::new(MailType::Ascii);
967 {
968 let mut handle = encoder.writer();
969 assert_ok!(handle.write_str(SoftAsciiStr::from_unchecked("12")));
970 handle.commit_partial_header();
971 }
972 assert_eq!(encoder.as_slice(), b"12");
973 }
974
975 #[test]
976 fn undo_does_undo() {
977 let mut encoder = EncodingBuffer::new(MailType::Ascii);
978 {
979 let mut handle = encoder.writer();
980 assert_ok!(
981 handle.write_str(SoftAsciiStr::from_unchecked("Header-One: 12")));
982 handle.undo_header();
983 }
984 assert_eq!(encoder.as_slice(), b"");
985 }
986
987 #[test]
988 fn undo_does_not_undo_to_much() {
989 let mut encoder = EncodingBuffer::new(MailType::Ascii);
990 {
991 let mut handle = encoder.writer();
992 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One: 12").unwrap()));
993 handle.finish_header();
994 assert_ok!(handle.write_str(SoftAsciiStr::from_str("ups: sa").unwrap()));
995 handle.undo_header();
996 }
997 assert_eq!(encoder.as_slice(), b"Header-One: 12\r\n");
998 }
999
1000 #[test]
1001 fn finish_adds_crlf_if_needed() {
1002 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1003 {
1004 let mut handle = encoder.writer();
1005 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One: 12").unwrap()));
1006 handle.finish_header();
1007 }
1008 assert_eq!(encoder.as_slice(), b"Header-One: 12\r\n");
1009 }
1010
1011 #[test]
1012 fn finish_does_not_add_crlf_if_not_needed() {
1013 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1014 {
1015 let mut handle = encoder.writer();
1016 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One: 12\r\n").unwrap()));
1017 handle.finish_header();
1018 }
1019 assert_eq!(encoder.as_slice(), b"Header-One: 12\r\n");
1020 }
1021
1022 #[test]
1023 fn finish_does_truncat_if_needed() {
1024 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1025 {
1026 let mut handle = encoder.writer();
1027 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One: 12\r\n ").unwrap()));
1028 handle.finish_header();
1029 }
1030 assert_eq!(encoder.as_slice(), b"Header-One: 12\r\n");
1031 }
1032
1033
1034 #[test]
1035 fn finish_can_handle_fws() {
1036 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1037 {
1038 let mut handle = encoder.writer();
1039 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One: 12 +\r\n 4").unwrap()));
1040 handle.finish_header();
1041 }
1042 assert_eq!(encoder.as_slice(), b"Header-One: 12 +\r\n 4\r\n");
1043 }
1044
1045 #[test]
1046 fn finish_only_truncats_if_needed() {
1047 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1048 {
1049 let mut handle = encoder.writer();
1050 assert_ok!(handle.write_str(
1051 SoftAsciiStr::from_str("Header-One: 12 +\r\n 4 ").unwrap()));
1052 handle.finish_header();
1053 }
1054 assert_eq!(encoder.as_slice(), b"Header-One: 12 +\r\n 4 \r\n");
1055 }
1056
1057
1058 #[test]
1059 fn orphan_lf_error() {
1060 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1061 {
1062 let mut handle = encoder.writer();
1063 assert_err!(handle.write_str(SoftAsciiStr::from_str("H: \na").unwrap()));
1064 handle.undo_header()
1065 }
1066 }
1067 #[test]
1068 fn orphan_cr_error() {
1069 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1070 {
1071 let mut handle = encoder.writer();
1072 assert_err!(handle.write_str(SoftAsciiStr::from_str("H: \ra").unwrap()));
1073 handle.undo_header()
1074 }
1075 }
1076
1077 #[test]
1078 fn orphan_trailing_lf() {
1079 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1080 {
1081 let mut handle = encoder.writer();
1082 assert_err!(handle.write_str(SoftAsciiStr::from_str("H: a\n").unwrap()));
1083 handle.undo_header();
1084 }
1085 }
1086
1087 #[test]
1088 fn orphan_trailing_cr() {
1089 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1090 {
1091 let mut handle = encoder.writer();
1092 assert_ok!(handle.write_str(SoftAsciiStr::from_str("H: a\r").unwrap()));
1093 handle.finish_header();
1096 }
1097 assert_eq!(encoder.as_slice(), b"H: a\r\n");
1098 }
1099
1100 #[test]
1101 fn soft_line_limit_can_be_breached() {
1102 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1103 {
1104 let mut handle = encoder.writer();
1105 for _ in 0u32..500 {
1106 assert_ok!(handle.internal_write_char("a"));
1107 }
1108 handle.finish_header();
1109 }
1110 }
1111
1112 #[test]
1113 fn hard_line_limit_can_not_be_breached() {
1114 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1115 {
1116 let mut handle = encoder.writer();
1117 for _ in 0u32..998 {
1118 assert_ok!(handle.internal_write_char("a"));
1119 }
1120
1121 assert_err!(handle.internal_write_char("b"));
1122 handle.finish_header();
1123 }
1124 }
1125
1126 #[test]
1127 fn break_line_on_fws() {
1128 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1129 {
1130 let mut handle = encoder.writer();
1131 assert_ok!(handle.write_str(SoftAsciiStr::from_str("A23456789:").unwrap()));
1132 handle.mark_fws_pos();
1133 assert_ok!(handle.write_str(SoftAsciiStr::from_str(concat!(
1134 "20_3456789",
1135 "30_3456789",
1136 "40_3456789",
1137 "50_3456789",
1138 "60_3456789",
1139 "70_3456789",
1140 "12345678XX"
1141 )).unwrap()));
1142 handle.finish_header();
1143 }
1144 assert_eq!(
1145 encoder.as_str().unwrap(),
1146 concat!(
1147 "A23456789:\r\n ",
1148 "20_3456789",
1149 "30_3456789",
1150 "40_3456789",
1151 "50_3456789",
1152 "60_3456789",
1153 "70_3456789",
1154 "12345678XX\r\n"
1155 )
1156 );
1157 }
1158
1159 #[test]
1160 fn break_line_on_fws_does_not_insert_unessesary_space() {
1161 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1162 {
1163 let mut handle = encoder.writer();
1164 assert_ok!(handle.write_str(SoftAsciiStr::from_str("A23456789:").unwrap()));
1165 handle.write_fws();
1166 assert_ok!(handle.write_str(SoftAsciiStr::from_str(concat!(
1167 "20_3456789",
1168 "30_3456789",
1169 "40_3456789",
1170 "50_3456789",
1171 "60_3456789",
1172 "70_3456789",
1173 "12345678XX"
1174 )).unwrap()));
1175 handle.finish_header();
1176 }
1177
1178 assert_eq!(
1179 encoder.as_str().unwrap(),
1180 concat!(
1181 "A23456789:\r\n ",
1182 "20_3456789",
1183 "30_3456789",
1184 "40_3456789",
1185 "50_3456789",
1186 "60_3456789",
1187 "70_3456789",
1188 "12345678XX\r\n"
1189 )
1190 );
1191 }
1192
1193
1194 #[test]
1195 fn to_long_unbreakable_line() {
1196 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1197 {
1198 let mut handle = encoder.writer();
1199 assert_ok!(handle.write_str(SoftAsciiStr::from_str("A23456789:").unwrap()));
1200 handle.mark_fws_pos();
1201 assert_ok!(handle.write_str(SoftAsciiStr::from_str(concat!(
1202 "10_3456789",
1203 "20_3456789",
1204 "30_3456789",
1205 "40_3456789",
1206 "50_3456789",
1207 "60_3456789",
1208 "70_3456789",
1209 "80_3456789",
1210 "90_3456789",
1211 "00_3456789",
1212 )).unwrap()));
1213 handle.finish_header();
1214 }
1215 assert_eq!(
1216 encoder.as_str().unwrap(),
1217 concat!(
1218 "A23456789:\r\n ",
1219 "10_3456789",
1220 "20_3456789",
1221 "30_3456789",
1222 "40_3456789",
1223 "50_3456789",
1224 "60_3456789",
1225 "70_3456789",
1226 "80_3456789",
1227 "90_3456789",
1228 "00_3456789\r\n",
1229 )
1230 );
1231 }
1232
1233 #[test]
1234 fn multiple_lines_breaks() {
1235 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1236 {
1237 let mut handle = encoder.writer();
1238 assert_ok!(handle.write_str(SoftAsciiStr::from_str("A23456789:").unwrap()));
1239 handle.mark_fws_pos();
1240 assert_ok!(handle.write_str(SoftAsciiStr::from_str(concat!(
1241 "10_3456789",
1242 "20_3456789",
1243 "30_3456789",
1244 "40_3456789",
1245 "50_3456789",
1246 "60_3456789",
1247 "70_3456789",
1248 )).unwrap()));
1249 handle.mark_fws_pos();
1250 assert_ok!(handle.write_str(SoftAsciiStr::from_str(concat!(
1251 "10_3456789",
1252 "20_3456789",
1253 "30_3456789",
1254 "40_3456789",
1255 )).unwrap()));
1256 handle.finish_header();
1257 }
1258 assert_eq!(
1259 encoder.as_str().unwrap(),
1260 concat!(
1261 "A23456789:\r\n ",
1262 "10_3456789",
1263 "20_3456789",
1264 "30_3456789",
1265 "40_3456789",
1266 "50_3456789",
1267 "60_3456789",
1268 "70_3456789\r\n ",
1269 "10_3456789",
1270 "20_3456789",
1271 "30_3456789",
1272 "40_3456789\r\n",
1273 )
1274 );
1275 }
1276
1277 #[test]
1278 fn hard_line_limit() {
1279 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1280 {
1281 let mut handle = encoder.writer();
1282 for x in 0..998 {
1283 if let Err(_) = handle.write_char(SoftAsciiChar::from_unchecked('X')) {
1284 panic!("error when writing char nr.: {:?}", x+1)
1285 }
1286 }
1287 let res = &[
1288 handle.write_char(SoftAsciiChar::from_unchecked('X')).is_err(),
1289 handle.write_char(SoftAsciiChar::from_unchecked('X')).is_err(),
1290 handle.write_char(SoftAsciiChar::from_unchecked('X')).is_err(),
1291 handle.write_char(SoftAsciiChar::from_unchecked('X')).is_err(),
1292 ];
1293 assert_eq!(
1294 res, &[true, true, true, true]
1295 );
1296 handle.undo_header();
1297 }
1298 }
1299
1300 #[test]
1301 fn write_utf8_fail_on_ascii_mail() {
1302 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1303 {
1304 let mut handle = encoder.writer();
1305 assert_err!(handle.write_utf8("↓"));
1306 handle.undo_header();
1307 }
1308 }
1309
1310 #[test]
1311 fn write_utf8_ascii_string_fail_on_ascii_mail() {
1312 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1313 {
1314 let mut handle = encoder.writer();
1315 assert_err!(handle.write_utf8("just_ascii"));
1316 handle.undo_header();
1317 }
1318 }
1319
1320 #[test]
1321 fn write_utf8_ok_on_internationalized_mail() {
1322 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1323 {
1324 let mut handle = encoder.writer();
1325 assert_ok!(handle.write_utf8("❤"));
1326 handle.finish_header();
1327 }
1328 assert_eq!(encoder.as_str().unwrap(), "❤\r\n");
1329 }
1330
1331 #[test]
1332 fn try_write_atext_ascii() {
1333 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1334 {
1335 let mut handle = encoder.writer();
1336 assert_ok!(handle.write_if_atext("hoho")
1337 .handle_condition_failure(|_|panic!("no condition failur expected")));
1338 let mut had_cond_failure = false;
1339 assert_ok!(handle.write_if_atext("a(b")
1340 .handle_condition_failure(|_| {had_cond_failure=true; Ok(())}));
1341 assert!(had_cond_failure);
1342 assert_ok!(handle.write_if_atext("")
1343 .handle_condition_failure(|_|panic!("no condition failur expected")));
1344 handle.finish_header();
1345 }
1346 assert_eq!(encoder.as_slice(), b"hoho\r\n");
1347 }
1348
1349 #[test]
1350 fn try_write_atext_internationalized() {
1351 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1352 {
1353 let mut handle = encoder.writer();
1354 assert_ok!(handle.write_if_atext("hoho")
1355 .handle_condition_failure(|_|panic!("no condition failur expected")));
1356 let mut had_cond_failure = false;
1357 assert_ok!(handle.write_if_atext("a(b")
1358 .handle_condition_failure(|_| {had_cond_failure=true; Ok(())}));
1359 assert!(had_cond_failure);
1360 assert_ok!(handle.write_if_atext("❤")
1361 .handle_condition_failure(|_|panic!("no condition failur expected")));
1362 handle.finish_header();
1363 }
1364 assert_eq!(encoder.as_str().unwrap(), "hoho❤\r\n");
1365 }
1366
1367 #[test]
1368 fn multiple_finish_calls_are_ok() {
1369 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1370 {
1371 let mut handle = encoder.writer();
1372 assert_ok!(handle.write_if_atext("hoho")
1373 .handle_condition_failure(|_|panic!("no condition failur expected")));
1374 let mut had_cond_failure = false;
1375 assert_ok!(handle.write_if_atext("a(b")
1376 .handle_condition_failure(|_| {had_cond_failure=true; Ok(())}));
1377 assert!(had_cond_failure);
1378 assert_ok!(handle.write_if_atext("❤")
1379 .handle_condition_failure(|_|panic!("no condition failur expected")));
1380 handle.finish_header();
1381 handle.finish_header();
1382 handle.finish_header();
1383 handle.finish_header();
1384 }
1385 assert_eq!(encoder.as_str().unwrap(), "hoho❤\r\n");
1386 }
1387
1388 #[test]
1389 fn multiple_finish_and_undo_calls() {
1390 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1391 {
1392 let mut handle = encoder.writer();
1393 assert_ok!(handle.write_if_atext("hoho")
1394 .handle_condition_failure(|_|panic!("no condition failur expected")));
1395 handle.undo_header();
1396 handle.finish_header();
1397 handle.undo_header();
1398 handle.undo_header();
1399 }
1400 assert_eq!(encoder.as_slice(), b"");
1401 }
1402
1403 #[test]
1404 fn header_body_header() {
1405 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1406 {
1407 let mut handle = encoder.writer();
1408 assert_ok!(handle.write_utf8("H: yay"));
1409 handle.finish_header();
1410 }
1411 encoder.write_body_unchecked(&"da body");
1412 {
1413 let mut handle = encoder.writer();
1414 assert_ok!(handle.write_utf8("❤"));
1415 handle.finish_header();
1416 }
1417 assert_eq!(
1418 encoder.as_slice(),
1419 concat!(
1420 "H: yay\r\n",
1421 "da body\r\n",
1422 "❤\r\n"
1423 ).as_bytes()
1424 );
1425 }
1426
1427 #[test]
1428 fn has_unfinished_parts() {
1429 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1430 {
1431 let mut handle = encoder.writer();
1432 assert_ok!(handle.write_utf8("Abc:"));
1433 assert!(handle.has_unfinished_parts());
1434 handle.undo_header();
1435 assert_not!(handle.has_unfinished_parts());
1436 assert_ok!(handle.write_utf8("Abc: c"));
1437 assert!(handle.has_unfinished_parts());
1438 handle.finish_header();
1439 assert_not!(handle.has_unfinished_parts());
1440 }
1441 }
1442
1443 #[test]
1444 fn drop_without_write_is_ok() {
1445 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1446 let handle = encoder.writer();
1447 mem::drop(handle)
1448 }
1449
1450 #[test]
1451 fn drop_after_undo_is_ok() {
1452 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1453 let mut handle = encoder.writer();
1454 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One").unwrap()));
1455 handle.undo_header();
1456 mem::drop(handle);
1457 }
1458
1459 #[test]
1460 fn drop_after_finish_is_ok() {
1461 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1462 let mut handle = encoder.writer();
1463 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One: 12").unwrap()));
1464 handle.finish_header();
1465 mem::drop(handle);
1466 }
1467
1468 #[should_panic]
1469 #[test]
1470 fn drop_unfinished_panics() {
1471 let mut encoder = EncodingBuffer::new(MailType::Ascii);
1472 let mut handle = encoder.writer();
1473 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header-One:").unwrap()));
1474 mem::drop(handle);
1475 }
1476
1477 #[test]
1478 fn trace_and_undo() {
1479 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1480 {
1481 let mut handle = encoder.writer();
1482 assert_ok!(handle.write_utf8("something"));
1483 handle.mark_fws_pos();
1484 assert_ok!(handle.write_utf8("<else>"));
1485 handle.undo_header();
1486 }
1487 assert_eq!(encoder.trace.len(), 0);
1488 }
1489
1490 #[test]
1491 fn trace_and_undo_does_do_to_much() {
1492 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1493 {
1494 let mut handle = encoder.writer();
1495 assert_ok!(handle.write_utf8("H: a"));
1496 handle.finish_header();
1497 assert_ok!(handle.write_utf8("something"));
1498 handle.mark_fws_pos();
1499 assert_ok!(handle.write_utf8("<else>"));
1500 handle.undo_header();
1501 }
1502 assert_eq!(encoder.trace, vec![
1503 NowUtf8,
1504 Text("H: a".into()),
1505 CRLF,
1506 End
1507 ]);
1508 }
1509
1510 #[test]
1511 fn trace_traces() {
1512 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1513 {
1514 let mut handle = encoder.writer();
1515 assert_ok!(handle.write_str(SoftAsciiStr::from_str("Header").unwrap()));
1516 assert_ok!(handle.write_char(SoftAsciiChar::from_unchecked(':')));
1517 let mut had_cond_failure = false;
1518 assert_ok!(handle.write_if_atext("a(b)c")
1519 .handle_condition_failure(|_|{had_cond_failure=true; Ok(())}));
1520 assert_ok!(handle.write_if_atext("abc")
1521 .handle_condition_failure(|_|panic!("unexpected cond failure")));
1522 assert_ok!(handle.write_utf8("❤"));
1523 assert_ok!(handle.write_str_unchecked("remove me\r\n"));
1524 assert_ok!(handle.write_utf8(" "));
1525 handle.finish_header()
1526 }
1527 assert_eq!(encoder.trace, vec![
1528 NowStr,
1529 Text("Header".into()),
1530 NowChar,
1531 Text(":".into()),
1532 NowAText,
1533 Text("abc".into()),
1534 NowUtf8,
1535 Text("❤".into()),
1536 NowUnchecked,
1537 Text("remove me".into()),
1538 CRLF,
1539 NowUtf8,
1540 Text(" ".into()),
1541 TruncateToCRLF,
1542 End
1543 ]);
1544 }
1545
1546 #[test]
1547 fn with_handle_on_error() {
1548 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1549 let res = encoder.write_header_line(|hdl| {
1550 hdl.write_utf8("some partial writes")?;
1551 Err(EncodingErrorKind::Other { kind: "error ;=)" }.into())
1552 });
1553 assert_err!(res);
1554 assert_eq!(encoder.trace, vec![]);
1555 assert_eq!(encoder.as_slice(), b"");
1556 }
1557
1558 #[test]
1559 fn with_handle_partial_writes() {
1560 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1561 let res = encoder.write_header_line(|hdl| {
1562 hdl.write_utf8("X-A: 12")
1563 });
1564 assert_ok!(res);
1565 assert_eq!(encoder.trace, vec![
1566 NowUtf8,
1567 Text("X-A: 12".into()),
1568 CRLF,
1569 End
1570 ]);
1571 assert_eq!(encoder.as_slice(), b"X-A: 12\r\n");
1572 }
1573
1574 #[test]
1575 fn with_handle_ok() {
1576 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1577 let res = encoder.write_header_line(|hdl| {
1578 hdl.write_utf8("X-A: 12")?;
1579 hdl.finish_header();
1580 Ok(())
1581 });
1582 assert_ok!(res);
1583 assert_eq!(encoder.trace, vec![
1584 NowUtf8,
1585 Text("X-A: 12".into()),
1586 CRLF,
1587 End,
1588 ]);
1589 assert_eq!(encoder.as_slice(), b"X-A: 12\r\n")
1590 }
1591
1592 #[test]
1593 fn douple_write_fws() {
1594 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1595 let res = encoder.write_header_line(|hdl| {
1596 hdl.write_fws();
1597 hdl.write_fws();
1598 Ok(())
1599 });
1600 assert_ok!(res);
1601 assert_eq!(encoder.trace, vec![
1602 MarkFWS, NowChar, Text(" ".to_owned()),
1603 MarkFWS, NowChar, Text(" ".to_owned()),
1604 TruncateToCRLF,
1605 End
1606 ]);
1607 assert_eq!(encoder.as_slice(), b"")
1608 }
1609
1610 #[test]
1611 fn double_write_fws_then_long_line() {
1612 let long_line = concat!(
1613 "10_3456789",
1614 "20_3456789",
1615 "30_3456789",
1616 "40_3456789",
1617 "50_3456789",
1618 "60_3456789",
1619 "70_3456789",
1620 "80_3456789",
1621 );
1622 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1623 let res = encoder.write_header_line(|hdl| {
1624 hdl.write_fws();
1625 hdl.write_fws();
1626 hdl.write_utf8(long_line)?;
1627 Ok(())
1628 });
1629 assert_ok!(res);
1630 assert_eq!(encoder.trace, vec![
1631 MarkFWS, NowChar, Text(" ".to_owned()),
1632 MarkFWS, NowChar, Text(" ".to_owned()),
1633 NowUtf8, Text(long_line.to_owned()),
1634 CRLF,
1635 End
1636 ]);
1637 assert_eq!(encoder.as_slice(), format!(" {}\r\n", long_line).as_bytes())
1638 }
1639
1640 #[test]
1641 fn semantic_ws_are_not_eaten_with_line_breaking() {
1642 let long_line_1 = concat!(
1643 "Header:789",
1644 "20_3456789",
1645 "30_3456789",
1646 "40_3456789",
1647 "50_3456789",
1648 "60_3456789",
1649 );
1650 let long_line_2 = concat!(
1651 " xxxxxxxxx",
1652 "80_3456789"
1653 );
1654
1655 let expected_res = concat!(
1656 "Header:789",
1657 "20_3456789",
1658 "30_3456789",
1659 "40_3456789",
1660 "50_3456789",
1661 "60_3456789",
1662 "\r\n ",
1663 " xxxxxxxxx",
1664 "80_3456789",
1665 "\r\n"
1666 );
1667
1668 let mut encoder = EncodingBuffer::new(MailType::Internationalized);
1669 encoder.write_header_line(|hdl| {
1670 hdl.write_utf8(long_line_1).unwrap();
1671 hdl.mark_fws_pos();
1672 hdl.write_utf8(long_line_2).unwrap();
1673 hdl.finish_header();
1674 Ok(())
1675 }).unwrap();
1676
1677 let got = str::from_utf8(encoder.as_slice()).unwrap();
1678 assert_eq!(expected_res, got);
1679 }
1680 }
1681
1682 ec_test! {
1683 does_ec_test_work,
1684 {
1685 use super::EncodingWriter;
1686 enc_func!(|x: &mut EncodingWriter| {
1687 x.write_utf8("hy")
1688 })
1689 } => Utf8 => [
1690 Text "hy"
1691 ]
1692 }
1693
1694 ec_test! {
1695 does_ec_test_work_with_encode_closure,
1696 {
1697 use super::EncodingWriter;
1698 let think = "hy";
1699 enc_closure!(move |x: &mut EncodingWriter| {
1700 x.write_utf8(think)
1701 })
1702 } => Utf8 => [
1703 Text "hy"
1704 ]
1705 }
1706
1707 ec_test! {
1708 does_ec_test_allow_early_return,
1709 {
1710 use super::EncodingWriter;
1711 if false { ec_bail!(kind: Other { kind: "if false ..." }) }
1713 enc_func!(|x: &mut EncodingWriter| {
1714 x.write_utf8("hy")
1715 })
1716 } => Utf8 => [
1717 Text "hy"
1718 ]
1719 }
1720
1721 mod trait_object {
1722 use super::super::*;
1723
1724 #[derive(Default, Clone, PartialEq, Debug)]
1725 struct TestType(&'static str);
1726
1727 impl EncodableInHeader for TestType {
1728 fn encode(&self, encoder: &mut EncodingWriter) -> Result<(), EncodingError> {
1729 encoder.write_utf8(self.0)
1730 }
1731
1732 fn boxed_clone(&self) -> Box<EncodableInHeader> {
1733 Box::new(self.clone())
1734 }
1735 }
1736
1737 #[derive(Default, Clone, PartialEq, Debug)]
1738 struct AnotherType(&'static str);
1739
1740 impl EncodableInHeader for AnotherType {
1741 fn encode(&self, encoder: &mut EncodingWriter) -> Result<(), EncodingError> {
1742 encoder.write_utf8(self.0)
1743 }
1744
1745 fn boxed_clone(&self) -> Box<EncodableInHeader> {
1746 Box::new(self.clone())
1747 }
1748 }
1749
1750 #[test]
1751 fn is() {
1752 let tt = TestType::default();
1753 let erased: &EncodableInHeader = &tt;
1754 assert_eq!( true, erased.is::<TestType>() );
1755 assert_eq!( false, erased.is::<AnotherType>());
1756 }
1757
1758 #[test]
1759 fn downcast_ref() {
1760 let tt = TestType::default();
1761 let erased: &EncodableInHeader = &tt;
1762 let res: Option<&TestType> = erased.downcast_ref::<TestType>();
1763 assert_eq!( Some(&tt), res );
1764 assert_eq!( None, erased.downcast_ref::<AnotherType>() );
1765 }
1766
1767 #[test]
1768 fn downcast_mut() {
1769 let mut tt_nr2 = TestType::default();
1770 let mut tt = TestType::default();
1771 let erased: &mut EncodableInHeader = &mut tt;
1772 {
1773 let res: Option<&mut TestType> = erased.downcast_mut::<TestType>();
1774 assert_eq!( Some(&mut tt_nr2), res );
1775 }
1776 assert_eq!( None, erased.downcast_mut::<AnotherType>() );
1777 }
1778
1779 #[test]
1780 fn downcast() {
1781 let tt = Box::new( TestType::default() );
1782 let erased: Box<EncodableInHeader> = tt;
1783 let erased = assert_err!(erased.downcast::<AnotherType>());
1784 let _: Box<TestType> = assert_ok!(erased.downcast::<TestType>());
1785 }
1786 }
1787}