logged_stream/
buffer_formatter.rs

1const DEFAULT_SEPARATOR: &str = ":";
2
3//////////////////////////////////////////////////////////////////////////////////////////////////////////////
4// Trait
5//////////////////////////////////////////////////////////////////////////////////////////////////////////////
6
7/// This trait allows to format bytes buffer using [`format_buffer`] method. It should be implemented for
8/// structures which are going to be used as formatting part inside [`LoggedStream`].
9///
10/// [`format_buffer`]: BufferFormatter::format_buffer
11/// [`LoggedStream`]: crate::LoggedStream
12pub trait BufferFormatter: Send + 'static {
13    /// This method returns a separator which will be inserted between bytes during [`format_buffer`] method call.
14    /// It should be implemented manually.
15    ///
16    /// [`format_buffer`]: BufferFormatter::format_buffer
17    fn get_separator(&self) -> &str;
18
19    /// This method accepts one byte from buffer and format it into [`String`]. It should be implemeted manually.
20    fn format_byte(&self, byte: &u8) -> String;
21
22    /// This method accepts bytes buffer and format it into [`String`]. It is automatically implemented method.
23    fn format_buffer(&self, buffer: &[u8]) -> String {
24        buffer
25            .iter()
26            .map(|b| self.format_byte(b))
27            .collect::<Vec<String>>()
28            .join(self.get_separator())
29    }
30}
31
32impl BufferFormatter for Box<dyn BufferFormatter> {
33    #[inline]
34    fn get_separator(&self) -> &str {
35        (**self).get_separator()
36    }
37
38    #[inline]
39    fn format_byte(&self, byte: &u8) -> String {
40        (**self).format_byte(byte)
41    }
42}
43
44//////////////////////////////////////////////////////////////////////////////////////////////////////////////
45// DecimalFormatter
46//////////////////////////////////////////////////////////////////////////////////////////////////////////////
47
48/// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in decimal number system.
49#[derive(Debug, Clone)]
50pub struct DecimalFormatter {
51    separator: String,
52}
53
54impl DecimalFormatter {
55    /// Construct a new instance of [`DecimalFormatter`] using provided borrowed separator. In case if provided
56    /// separator will be [`None`], than default separator (`:`) will be used.
57    pub fn new(provided_separator: Option<&str>) -> Self {
58        Self::new_owned(provided_separator.map(ToString::to_string))
59    }
60
61    /// Construct a new instance of [`DecimalFormatter`] using provided owned separator. In case if provided
62    /// separator will be [`None`], than default separator (`:`) will be used.
63    pub fn new_owned(provided_separator: Option<String>) -> Self {
64        Self {
65            separator: provided_separator.unwrap_or(DEFAULT_SEPARATOR.to_string()),
66        }
67    }
68
69    /// Construct a new instance of [`DecimalFormatter`] using default separator (`:`).
70    pub fn new_default() -> Self {
71        Self::new_owned(None)
72    }
73}
74
75impl BufferFormatter for DecimalFormatter {
76    #[inline]
77    fn get_separator(&self) -> &str {
78        self.separator.as_str()
79    }
80
81    #[inline]
82    fn format_byte(&self, byte: &u8) -> String {
83        format!("{byte}")
84    }
85}
86
87impl BufferFormatter for Box<DecimalFormatter> {
88    #[inline]
89    fn get_separator(&self) -> &str {
90        (**self).get_separator()
91    }
92
93    #[inline]
94    fn format_byte(&self, byte: &u8) -> String {
95        (**self).format_byte(byte)
96    }
97}
98
99impl Default for DecimalFormatter {
100    fn default() -> Self {
101        Self::new_default()
102    }
103}
104
105//////////////////////////////////////////////////////////////////////////////////////////////////////////////
106// OctalFormatter
107//////////////////////////////////////////////////////////////////////////////////////////////////////////////
108
109/// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in octal number system.
110#[derive(Debug, Clone)]
111pub struct OctalFormatter {
112    separator: String,
113}
114
115impl OctalFormatter {
116    /// Construct a new instance of [`OctalFormatter`] using provided borrowed separator. In case if provided
117    /// separator will be [`None`], than default separator (`:`) will be used.
118    pub fn new(provided_separator: Option<&str>) -> Self {
119        Self::new_owned(provided_separator.map(ToString::to_string))
120    }
121
122    /// Construct a new instance of [`OctalFormatter`] using provided owned separator. In case if provided
123    /// separator will be [`None`], than default separator (`:`) will be used.
124    pub fn new_owned(provided_separator: Option<String>) -> Self {
125        Self {
126            separator: provided_separator.unwrap_or(DEFAULT_SEPARATOR.to_string()),
127        }
128    }
129
130    /// Construct a new instance of [`OctalFormatter`] using default separator (`:`).
131    pub fn new_default() -> Self {
132        Self::new_owned(None)
133    }
134}
135
136impl BufferFormatter for OctalFormatter {
137    #[inline]
138    fn get_separator(&self) -> &str {
139        self.separator.as_str()
140    }
141
142    #[inline]
143    fn format_byte(&self, byte: &u8) -> String {
144        format!("{byte:03o}")
145    }
146}
147
148impl BufferFormatter for Box<OctalFormatter> {
149    #[inline]
150    fn get_separator(&self) -> &str {
151        (**self).get_separator()
152    }
153
154    #[inline]
155    fn format_byte(&self, byte: &u8) -> String {
156        (**self).format_byte(byte)
157    }
158}
159
160impl Default for OctalFormatter {
161    fn default() -> Self {
162        Self::new_default()
163    }
164}
165
166//////////////////////////////////////////////////////////////////////////////////////////////////////////////
167// UppercaseHexadecimalFormatter
168//////////////////////////////////////////////////////////////////////////////////////////////////////////////
169
170/// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in hexadecimal number system.
171#[derive(Debug, Clone)]
172pub struct UppercaseHexadecimalFormatter {
173    separator: String,
174}
175
176impl UppercaseHexadecimalFormatter {
177    /// Construct a new instance of [`UppercaseHexadecimalFormatter`] using provided borrowed separator. In case
178    /// if provided separator will be [`None`], than default separator (`:`) will be used.
179    pub fn new(provided_separator: Option<&str>) -> Self {
180        Self::new_owned(provided_separator.map(ToString::to_string))
181    }
182
183    /// Construct a new instance of [`UppercaseHexadecimalFormatter`] using provided owned separator. In case
184    /// if provided separator will be [`None`], than default separator (`:`) will be used.
185    pub fn new_owned(provided_separator: Option<String>) -> Self {
186        Self {
187            separator: provided_separator.unwrap_or(DEFAULT_SEPARATOR.to_string()),
188        }
189    }
190
191    /// Construct a new instance of [`UppercaseHexadecimalFormatter`] using default separator (`:`).
192    pub fn new_default() -> Self {
193        Self::new_owned(None)
194    }
195}
196
197impl BufferFormatter for UppercaseHexadecimalFormatter {
198    #[inline]
199    fn get_separator(&self) -> &str {
200        self.separator.as_str()
201    }
202
203    #[inline]
204    fn format_byte(&self, byte: &u8) -> String {
205        format!("{byte:02X}")
206    }
207}
208
209impl BufferFormatter for Box<UppercaseHexadecimalFormatter> {
210    #[inline]
211    fn get_separator(&self) -> &str {
212        (**self).get_separator()
213    }
214
215    #[inline]
216    fn format_byte(&self, byte: &u8) -> String {
217        (**self).format_byte(byte)
218    }
219}
220
221impl Default for UppercaseHexadecimalFormatter {
222    fn default() -> Self {
223        Self::new_default()
224    }
225}
226
227//////////////////////////////////////////////////////////////////////////////////////////////////////////////
228// LowercaseHexadecimalFormatter
229//////////////////////////////////////////////////////////////////////////////////////////////////////////////
230
231/// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in hexdecimal number system.
232#[derive(Debug, Clone)]
233pub struct LowercaseHexadecimalFormatter {
234    separator: String,
235}
236
237impl LowercaseHexadecimalFormatter {
238    /// Construct a new instance of [`LowercaseHexadecimalFormatter`] using provided borrowed separator. In case
239    /// if provided separator will be [`None`], than default separator (`:`) will be used.
240    pub fn new(provided_separator: Option<&str>) -> Self {
241        Self::new_owned(provided_separator.map(ToString::to_string))
242    }
243
244    /// Construct a new instance of [`LowercaseHexadecimalFormatter`] using provided owned separator. In case
245    /// if provided separator will be [`None`], than default separator (`:`) will be used.
246    pub fn new_owned(provided_separator: Option<String>) -> Self {
247        Self {
248            separator: provided_separator.unwrap_or(DEFAULT_SEPARATOR.to_string()),
249        }
250    }
251
252    /// Construct a new instance of [`LowercaseHexadecimalFormatter`] using default separator (`:`).
253    pub fn new_default() -> Self {
254        Self::new_owned(None)
255    }
256}
257
258impl BufferFormatter for LowercaseHexadecimalFormatter {
259    #[inline]
260    fn get_separator(&self) -> &str {
261        self.separator.as_str()
262    }
263
264    #[inline]
265    fn format_byte(&self, byte: &u8) -> String {
266        format!("{byte:02x}")
267    }
268}
269
270impl BufferFormatter for Box<LowercaseHexadecimalFormatter> {
271    #[inline]
272    fn get_separator(&self) -> &str {
273        (**self).get_separator()
274    }
275
276    #[inline]
277    fn format_byte(&self, byte: &u8) -> String {
278        (**self).format_byte(byte)
279    }
280}
281
282impl Default for LowercaseHexadecimalFormatter {
283    fn default() -> Self {
284        Self::new_default()
285    }
286}
287
288//////////////////////////////////////////////////////////////////////////////////////////////////////////////
289// BinaryFormatter
290//////////////////////////////////////////////////////////////////////////////////////////////////////////////
291
292/// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in binary number system.
293#[derive(Debug, Clone)]
294pub struct BinaryFormatter {
295    separator: String,
296}
297
298impl BinaryFormatter {
299    /// Construct a new instance of [`BinaryFormatter`] using provided borrowed separator. In case if provided
300    /// separator will be [`None`], than default separator (`:`) will be used.
301    pub fn new(provided_separator: Option<&str>) -> Self {
302        Self::new_owned(provided_separator.map(ToString::to_string))
303    }
304
305    /// Construct a new instance of [`BinaryFormatter`] using provided owned separator. In case if provided
306    /// separator will be [`None`], than default separator (`:`) will be used.
307    pub fn new_owned(provided_separator: Option<String>) -> Self {
308        Self {
309            separator: provided_separator.unwrap_or(DEFAULT_SEPARATOR.to_string()),
310        }
311    }
312
313    /// Construct a new instance of [`BinaryFormatter`] using default separator (`:`).
314    pub fn new_default() -> Self {
315        Self::new_owned(None)
316    }
317}
318
319impl BufferFormatter for BinaryFormatter {
320    #[inline]
321    fn get_separator(&self) -> &str {
322        self.separator.as_str()
323    }
324
325    #[inline]
326    fn format_byte(&self, byte: &u8) -> String {
327        format!("{byte:08b}")
328    }
329}
330
331impl BufferFormatter for Box<BinaryFormatter> {
332    #[inline]
333    fn get_separator(&self) -> &str {
334        (**self).get_separator()
335    }
336
337    #[inline]
338    fn format_byte(&self, byte: &u8) -> String {
339        (**self).format_byte(byte)
340    }
341}
342
343impl Default for BinaryFormatter {
344    fn default() -> Self {
345        Self::new_default()
346    }
347}
348
349//////////////////////////////////////////////////////////////////////////////////////////////////////////////
350// Tests
351//////////////////////////////////////////////////////////////////////////////////////////////////////////////
352
353#[cfg(test)]
354mod tests {
355    use crate::buffer_formatter::BinaryFormatter;
356    use crate::buffer_formatter::BufferFormatter;
357    use crate::buffer_formatter::DecimalFormatter;
358    use crate::buffer_formatter::LowercaseHexadecimalFormatter;
359    use crate::buffer_formatter::OctalFormatter;
360    use crate::buffer_formatter::UppercaseHexadecimalFormatter;
361
362    const FORMATTING_TEST_VALUES: &[u8] = &[10, 11, 12, 13, 14, 15, 16, 17, 18];
363
364    #[test]
365    fn test_buffer_formatting() {
366        let lowercase_hexadecimal = LowercaseHexadecimalFormatter::new_default();
367        let uppercase_hexadecimal = UppercaseHexadecimalFormatter::new_default();
368        let decimal = DecimalFormatter::new_default();
369        let octal = OctalFormatter::new_default();
370        let binary = BinaryFormatter::new_default();
371
372        assert_eq!(
373            lowercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
374            String::from("0a:0b:0c:0d:0e:0f:10:11:12")
375        );
376        assert_eq!(
377            uppercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
378            String::from("0A:0B:0C:0D:0E:0F:10:11:12")
379        );
380        assert_eq!(
381            decimal.format_buffer(FORMATTING_TEST_VALUES),
382            String::from("10:11:12:13:14:15:16:17:18")
383        );
384        assert_eq!(
385            octal.format_buffer(FORMATTING_TEST_VALUES),
386            String::from("012:013:014:015:016:017:020:021:022")
387        );
388        assert_eq!(
389            binary.format_buffer(FORMATTING_TEST_VALUES),
390            String::from(
391                "00001010:00001011:00001100:00001101:00001110:00001111:00010000:00010001:00010010"
392            )
393        );
394    }
395
396    #[test]
397    fn test_custom_separator() {
398        let lowercase_hexadecimal = LowercaseHexadecimalFormatter::new(Some("-"));
399        let uppercase_hexadecimal = UppercaseHexadecimalFormatter::new(Some("-"));
400        let decimal = DecimalFormatter::new(Some("-"));
401        let octal = OctalFormatter::new(Some("-"));
402        let binary = BinaryFormatter::new(Some("-"));
403
404        assert_eq!(
405            lowercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
406            String::from("0a-0b-0c-0d-0e-0f-10-11-12")
407        );
408        assert_eq!(
409            uppercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
410            String::from("0A-0B-0C-0D-0E-0F-10-11-12")
411        );
412        assert_eq!(
413            decimal.format_buffer(FORMATTING_TEST_VALUES),
414            String::from("10-11-12-13-14-15-16-17-18")
415        );
416        assert_eq!(
417            octal.format_buffer(FORMATTING_TEST_VALUES),
418            String::from("012-013-014-015-016-017-020-021-022")
419        );
420        assert_eq!(
421            binary.format_buffer(FORMATTING_TEST_VALUES),
422            String::from(
423                "00001010-00001011-00001100-00001101-00001110-00001111-00010000-00010001-00010010"
424            )
425        );
426    }
427
428    fn assert_unpin<T: Unpin>() {}
429
430    #[test]
431    fn test_unpin() {
432        assert_unpin::<BinaryFormatter>();
433        assert_unpin::<DecimalFormatter>();
434        assert_unpin::<LowercaseHexadecimalFormatter>();
435        assert_unpin::<UppercaseHexadecimalFormatter>();
436        assert_unpin::<OctalFormatter>();
437    }
438
439    #[test]
440    fn test_trait_object_safety() {
441        // Assert traint object construct.
442        let lowercase_hexadecimal: Box<dyn BufferFormatter> =
443            Box::new(LowercaseHexadecimalFormatter::new(None));
444        let uppercase_hexadecimal: Box<dyn BufferFormatter> =
445            Box::new(UppercaseHexadecimalFormatter::new(None));
446        let decimal: Box<dyn BufferFormatter> = Box::new(DecimalFormatter::new(None));
447        let octal: Box<dyn BufferFormatter> = Box::new(OctalFormatter::new(None));
448        let binary: Box<dyn BufferFormatter> = Box::new(BinaryFormatter::new(None));
449
450        // Assert that trait object methods are dispatchable.
451        _ = lowercase_hexadecimal.get_separator();
452        _ = lowercase_hexadecimal.format_buffer(b"qwertyuiop");
453
454        _ = uppercase_hexadecimal.get_separator();
455        _ = uppercase_hexadecimal.format_buffer(b"qwertyuiop");
456
457        _ = decimal.get_separator();
458        _ = decimal.format_buffer(b"qwertyuiop");
459
460        _ = octal.get_separator();
461        _ = octal.format_buffer(b"qwertyuiop");
462
463        _ = binary.get_separator();
464        _ = binary.format_buffer(b"qwertyuiop");
465    }
466
467    fn assert_buffer_formatter<T: BufferFormatter>() {}
468
469    #[test]
470    fn test_box() {
471        assert_buffer_formatter::<Box<dyn BufferFormatter>>();
472        assert_buffer_formatter::<Box<LowercaseHexadecimalFormatter>>();
473        assert_buffer_formatter::<Box<UppercaseHexadecimalFormatter>>();
474        assert_buffer_formatter::<Box<DecimalFormatter>>();
475        assert_buffer_formatter::<Box<OctalFormatter>>();
476        assert_buffer_formatter::<Box<BinaryFormatter>>();
477    }
478
479    fn assert_send<T: Send>() {}
480
481    #[test]
482    fn test_send() {
483        assert_send::<LowercaseHexadecimalFormatter>();
484        assert_send::<UppercaseHexadecimalFormatter>();
485        assert_send::<DecimalFormatter>();
486        assert_send::<OctalFormatter>();
487        assert_send::<BinaryFormatter>();
488
489        assert_send::<Box<dyn BufferFormatter>>();
490        assert_send::<Box<LowercaseHexadecimalFormatter>>();
491        assert_send::<Box<UppercaseHexadecimalFormatter>>();
492        assert_send::<Box<DecimalFormatter>>();
493        assert_send::<Box<OctalFormatter>>();
494        assert_send::<Box<BinaryFormatter>>();
495    }
496}