encoding/
types.rs

1/*!
2 * Reexports of types from encoding-types crate.
3 */
4
5pub use super::decode;
6pub use encoding_types::{
7    ByteWriter, CodecError, DecoderTrap, DecoderTrapFunc, EncoderTrap, EncoderTrapFunc, Encoding,
8    EncodingRef, RawDecoder, RawEncoder, StringWriter,
9};
10
11#[cfg(test)]
12mod tests {
13    use super::EncoderTrap::NcrEscape;
14    use super::*;
15    use crate::util::StrCharIndex;
16    use std::convert::Into;
17    use std::sync::mpsc::channel;
18
19    // a contrived encoding example: same as ASCII, but inserts `prepend` between each character
20    // within two "e"s (so that `widespread` becomes `wide*s*p*r*ead` and `eeeeasel` becomes
21    // `e*ee*ease*l` where `*` is substituted by `prepend`) and prohibits `prohibit` character.
22    struct MyEncoder {
23        flag: bool,
24        prohibit: char,
25        prepend: &'static str,
26        toggle: bool,
27    }
28    impl RawEncoder for MyEncoder {
29        fn from_self(&self) -> Box<dyn RawEncoder> {
30            Box::new(MyEncoder {
31                flag: self.flag,
32                prohibit: self.prohibit,
33                prepend: self.prepend,
34                toggle: false,
35            })
36        }
37        fn is_ascii_compatible(&self) -> bool {
38            self.flag
39        }
40        fn raw_feed(
41            &mut self,
42            input: &str,
43            output: &mut dyn ByteWriter,
44        ) -> (usize, Option<CodecError>) {
45            for ((i, j), ch) in input.index_iter() {
46                if ch <= '\u{7f}' && ch != self.prohibit {
47                    if self.toggle && !self.prepend.is_empty() {
48                        output.write_bytes(self.prepend.as_bytes());
49                    }
50                    output.write_byte(ch as u8);
51                    if ch == 'e' {
52                        self.toggle = !self.toggle;
53                    }
54                } else {
55                    return (
56                        i,
57                        Some(CodecError {
58                            upto: j as isize,
59                            cause: "!!!".into(),
60                        }),
61                    );
62                }
63            }
64            (input.len(), None)
65        }
66        fn raw_finish(&mut self, _output: &mut dyn ByteWriter) -> Option<CodecError> {
67            None
68        }
69    }
70
71    struct MyEncoding {
72        flag: bool,
73        prohibit: char,
74        prepend: &'static str,
75    }
76    impl Encoding for MyEncoding {
77        fn name(&self) -> &'static str {
78            "my encoding"
79        }
80        fn raw_encoder(&self) -> Box<dyn RawEncoder> {
81            Box::new(MyEncoder {
82                flag: self.flag,
83                prohibit: self.prohibit,
84                prepend: self.prepend,
85                toggle: false,
86            })
87        }
88        fn raw_decoder(&self) -> Box<dyn RawDecoder> {
89            panic!("not supported")
90        }
91    }
92
93    #[test]
94    fn test_encoding_debug_format() {
95        let enc = MyEncoding {
96            flag: true,
97            prohibit: '\u{80}',
98            prepend: "",
99        };
100
101        assert_eq!(
102            format!("{:?}", &enc as &dyn Encoding),
103            "Encoding(my encoding)"
104        );
105    }
106
107    #[test]
108    fn test_reencoding_trap_with_ascii_compatible_encoding() {
109        static COMPAT: &MyEncoding = &MyEncoding {
110            flag: true,
111            prohibit: '\u{80}',
112            prepend: "",
113        };
114        static INCOMPAT: &MyEncoding = &MyEncoding {
115            flag: false,
116            prohibit: '\u{80}',
117            prepend: "",
118        };
119
120        assert_eq!(
121            COMPAT.encode("Hello\u{203d} I'm fine.", NcrEscape),
122            Ok(b"Hello&#8253; I'm fine.".to_vec())
123        );
124        assert_eq!(
125            INCOMPAT.encode("Hello\u{203d} I'm fine.", NcrEscape),
126            Ok(b"Hello&#8253; I'm fine.".to_vec())
127        );
128    }
129
130    #[test]
131    fn test_reencoding_trap_with_ascii_incompatible_encoding() {
132        static COMPAT: &MyEncoding = &MyEncoding {
133            flag: true,
134            prohibit: '\u{80}',
135            prepend: "*",
136        };
137        static INCOMPAT: &MyEncoding = &MyEncoding {
138            flag: false,
139            prohibit: '\u{80}',
140            prepend: "*",
141        };
142
143        // this should behave incorrectly as the encoding broke the assumption.
144        assert_eq!(
145            COMPAT.encode("Hello\u{203d} I'm fine.", NcrEscape),
146            Ok(b"He*l*l*o&#8253;* *I*'*m* *f*i*n*e.".to_vec())
147        );
148        assert_eq!(
149            INCOMPAT.encode("Hello\u{203d} I'm fine.", NcrEscape),
150            Ok(b"He*l*l*o*&*#*8*2*5*3*;* *I*'*m* *f*i*n*e.".to_vec())
151        );
152    }
153
154    #[test]
155    fn test_encoding_sendable() {
156        static COMPAT: &MyEncoding = &MyEncoding {
157            flag: true,
158            prohibit: '\u{80}',
159            prepend: "*",
160        };
161        let encoder = COMPAT.raw_encoder();
162        let (tx, _rx) = channel();
163        let _ = tx.send(encoder);
164    }
165
166    #[test]
167    #[should_panic]
168    fn test_reencoding_trap_can_fail() {
169        static FAIL: &MyEncoding = &MyEncoding {
170            flag: false,
171            prohibit: '&',
172            prepend: "",
173        };
174
175        // this should fail as this contrived encoding does not support `&` at all
176        let _ = FAIL.encode("Hello\u{203d} I'm fine.", NcrEscape);
177    }
178}