Skip to main content

logged_stream/
buffer_formatter.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::sync::Arc;
4
5const DEFAULT_SEPARATOR: &str = ":";
6
7//////////////////////////////////////////////////////////////////////////////////////////////////////////////
8// Trait
9//////////////////////////////////////////////////////////////////////////////////////////////////////////////
10
11/// This trait allows to format bytes buffer using [`format_buffer`] method. It should be implemented for
12/// structures which are going to be used as formatting part inside [`LoggedStream`].
13///
14/// # Wrapping in an `Arc`
15///
16/// `BufferFormatter` is implemented for `Box<dyn BufferFormatter>` unconditionally, but the blanket
17/// impl for `Arc<T>` additionally requires `T: Sync` — because `BufferFormatter` requires `Send`,
18/// and `Arc<T>` is `Send` only when `T: Send + Sync`. The concrete formatters in this crate are
19/// `Sync`, so `Arc<LowercaseHexadecimalFormatter>` and friends work directly. For an erased trait
20/// object you must spell the bound out as `Arc<dyn BufferFormatter + Sync>`; a plain
21/// `Arc<dyn BufferFormatter>` does **not** implement `BufferFormatter`.
22///
23/// [`format_buffer`]: BufferFormatter::format_buffer
24/// [`LoggedStream`]: crate::LoggedStream
25pub trait BufferFormatter: Send + 'static {
26    /// This method returns a separator which will be inserted between bytes during [`format_buffer`] method call.
27    /// It should be implemented manually.
28    ///
29    /// [`format_buffer`]: BufferFormatter::format_buffer
30    fn get_separator(&self) -> &str;
31
32    /// This method accepts one byte from buffer and format it into [`String`]. It should be implemeted manually.
33    fn format_byte(&self, byte: &u8) -> String;
34
35    /// This method accepts bytes buffer and format it into [`String`]. It is automatically implemented method.
36    fn format_buffer(&self, buffer: &[u8]) -> String {
37        buffer
38            .iter()
39            .map(|b| self.format_byte(b))
40            .collect::<Vec<String>>()
41            .join(self.get_separator())
42    }
43}
44
45impl BufferFormatter for Box<dyn BufferFormatter> {
46    #[inline]
47    fn get_separator(&self) -> &str {
48        (**self).get_separator()
49    }
50
51    #[inline]
52    fn format_byte(&self, byte: &u8) -> String {
53        (**self).format_byte(byte)
54    }
55}
56
57impl<T: BufferFormatter + ?Sized + Sync> BufferFormatter for Arc<T> {
58    #[inline]
59    fn get_separator(&self) -> &str {
60        (**self).get_separator()
61    }
62
63    #[inline]
64    fn format_byte(&self, byte: &u8) -> String {
65        (**self).format_byte(byte)
66    }
67}
68
69//////////////////////////////////////////////////////////////////////////////////////////////////////////////
70// Macro for Formatter Generation
71//////////////////////////////////////////////////////////////////////////////////////////////////////////////
72
73macro_rules! define_formatter {
74    (
75        $(#[$struct_meta:meta])*
76        $name:ident,
77        $format_expr:expr
78    ) => {
79        $(#[$struct_meta])*
80        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
81        pub struct $name {
82            separator: Cow<'static, str>,
83        }
84
85        impl $name {
86            /// Construct a new instance of
87            #[doc = concat!("`", stringify!($name), "`")]
88            /// using provided borrowed separator. In case if provided
89            /// separator will be [`None`], then default separator (`:`) will be used.
90            pub fn new(provided_separator: Option<&str>) -> Self {
91                Self {
92                    separator: provided_separator
93                        .map(|s| Cow::Owned(s.to_string()))
94                        .unwrap_or(Cow::Borrowed(DEFAULT_SEPARATOR)),
95                }
96            }
97
98            /// Construct a new instance of
99            #[doc = concat!("`", stringify!($name), "`")]
100            /// using provided static borrowed separator. In case if provided
101            /// separator will be [`None`], then default separator (`:`) will be used. This method avoids allocation for
102            /// static string separators.
103            pub fn new_static(provided_separator: Option<&'static str>) -> Self {
104                Self {
105                    separator: provided_separator
106                        .map(Cow::Borrowed)
107                        .unwrap_or(Cow::Borrowed(DEFAULT_SEPARATOR)),
108                }
109            }
110
111            /// Construct a new instance of
112            #[doc = concat!("`", stringify!($name), "`")]
113            /// using provided owned separator. In case if provided
114            /// separator will be [`None`], then default separator (`:`) will be used.
115            pub fn new_owned(provided_separator: Option<String>) -> Self {
116                Self {
117                    separator: provided_separator
118                        .map(Cow::Owned)
119                        .unwrap_or(Cow::Borrowed(DEFAULT_SEPARATOR)),
120                }
121            }
122
123            /// Construct a new instance of
124            #[doc = concat!("`", stringify!($name), "`")]
125            /// using default separator (`:`).
126            pub fn new_default() -> Self {
127                Self {
128                    separator: Cow::Borrowed(DEFAULT_SEPARATOR),
129                }
130            }
131        }
132
133        impl BufferFormatter for $name {
134            #[inline]
135            fn get_separator(&self) -> &str {
136                &self.separator
137            }
138
139            #[inline]
140            fn format_byte(&self, byte: &u8) -> String {
141                $format_expr(byte)
142            }
143        }
144
145        impl BufferFormatter for Box<$name> {
146            #[inline]
147            fn get_separator(&self) -> &str {
148                (**self).get_separator()
149            }
150
151            #[inline]
152            fn format_byte(&self, byte: &u8) -> String {
153                (**self).format_byte(byte)
154            }
155        }
156
157        impl Default for $name {
158            fn default() -> Self {
159                Self::new_default()
160            }
161        }
162
163        impl From<Cow<'static, str>> for $name {
164            fn from(separator: Cow<'static, str>) -> Self {
165                Self { separator }
166            }
167        }
168
169        impl From<&str> for $name {
170            fn from(separator: &str) -> Self {
171                Self::new(Some(separator))
172            }
173        }
174
175        impl From<String> for $name {
176            fn from(separator: String) -> Self {
177                Self::new_owned(Some(separator))
178            }
179        }
180
181        impl From<Option<&str>> for $name {
182            fn from(separator: Option<&str>) -> Self {
183                Self::new(separator)
184            }
185        }
186
187        impl From<Option<String>> for $name {
188            fn from(separator: Option<String>) -> Self {
189                Self::new_owned(separator)
190            }
191        }
192
193        impl fmt::Display for $name {
194            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195                write!(f, "{}(separator: {:?})", stringify!($name), self.separator)
196            }
197        }
198    };
199}
200
201//////////////////////////////////////////////////////////////////////////////////////////////////////////////
202// Formatters definitions
203//////////////////////////////////////////////////////////////////////////////////////////////////////////////
204
205define_formatter!(
206    /// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in decimal number system.
207    DecimalFormatter,
208    |byte: &u8| format!("{byte}")
209);
210
211define_formatter!(
212    /// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in octal number system.
213    OctalFormatter,
214    |byte: &u8| format!("{byte:03o}")
215);
216
217define_formatter!(
218    /// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in hexadecimal number system.
219    UppercaseHexadecimalFormatter,
220    |byte: &u8| format!("{byte:02X}")
221);
222
223define_formatter!(
224    /// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in hexadecimal number system.
225    LowercaseHexadecimalFormatter,
226    |byte: &u8| format!("{byte:02x}")
227);
228
229define_formatter!(
230    /// This implementation of [`BufferFormatter`] trait formats provided bytes buffer in binary number system.
231    BinaryFormatter,
232    |byte: &u8| format!("{byte:08b}")
233);
234
235//////////////////////////////////////////////////////////////////////////////////////////////////////////////
236// Tests
237//////////////////////////////////////////////////////////////////////////////////////////////////////////////
238
239#[cfg(test)]
240mod tests {
241    use crate::buffer_formatter::BinaryFormatter;
242    use crate::buffer_formatter::BufferFormatter;
243    use crate::buffer_formatter::DecimalFormatter;
244    use crate::buffer_formatter::LowercaseHexadecimalFormatter;
245    use crate::buffer_formatter::OctalFormatter;
246    use crate::buffer_formatter::UppercaseHexadecimalFormatter;
247    use std::borrow::Cow;
248    use std::sync::Arc;
249    use std::thread;
250
251    const FORMATTING_TEST_VALUES: &[u8] = &[10, 11, 12, 13, 14, 15, 16, 17, 18];
252
253    #[test]
254    fn test_buffer_formatting() {
255        let lowercase_hexadecimal = LowercaseHexadecimalFormatter::new_default();
256        let uppercase_hexadecimal = UppercaseHexadecimalFormatter::new_default();
257        let decimal = DecimalFormatter::new_default();
258        let octal = OctalFormatter::new_default();
259        let binary = BinaryFormatter::new_default();
260
261        assert_eq!(
262            lowercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
263            String::from("0a:0b:0c:0d:0e:0f:10:11:12")
264        );
265        assert_eq!(
266            uppercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
267            String::from("0A:0B:0C:0D:0E:0F:10:11:12")
268        );
269        assert_eq!(
270            decimal.format_buffer(FORMATTING_TEST_VALUES),
271            String::from("10:11:12:13:14:15:16:17:18")
272        );
273        assert_eq!(
274            octal.format_buffer(FORMATTING_TEST_VALUES),
275            String::from("012:013:014:015:016:017:020:021:022")
276        );
277        assert_eq!(
278            binary.format_buffer(FORMATTING_TEST_VALUES),
279            String::from(
280                "00001010:00001011:00001100:00001101:00001110:00001111:00010000:00010001:00010010"
281            )
282        );
283    }
284
285    #[test]
286    fn test_custom_separator() {
287        let lowercase_hexadecimal = LowercaseHexadecimalFormatter::new(Some("-"));
288        let uppercase_hexadecimal = UppercaseHexadecimalFormatter::new(Some("-"));
289        let decimal = DecimalFormatter::new(Some("-"));
290        let octal = OctalFormatter::new(Some("-"));
291        let binary = BinaryFormatter::new(Some("-"));
292
293        assert_eq!(
294            lowercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
295            String::from("0a-0b-0c-0d-0e-0f-10-11-12")
296        );
297        assert_eq!(
298            uppercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
299            String::from("0A-0B-0C-0D-0E-0F-10-11-12")
300        );
301        assert_eq!(
302            decimal.format_buffer(FORMATTING_TEST_VALUES),
303            String::from("10-11-12-13-14-15-16-17-18")
304        );
305        assert_eq!(
306            octal.format_buffer(FORMATTING_TEST_VALUES),
307            String::from("012-013-014-015-016-017-020-021-022")
308        );
309        assert_eq!(
310            binary.format_buffer(FORMATTING_TEST_VALUES),
311            String::from(
312                "00001010-00001011-00001100-00001101-00001110-00001111-00010000-00010001-00010010"
313            )
314        );
315    }
316
317    #[test]
318    fn test_static_separator() {
319        // new_static should produce same results as new, just without allocation
320        let lowercase_hexadecimal = LowercaseHexadecimalFormatter::new_static(Some("-"));
321        let uppercase_hexadecimal = UppercaseHexadecimalFormatter::new_static(Some("-"));
322        let decimal = DecimalFormatter::new_static(Some("-"));
323        let octal = OctalFormatter::new_static(Some("-"));
324        let binary = BinaryFormatter::new_static(Some("-"));
325
326        assert_eq!(
327            lowercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
328            String::from("0a-0b-0c-0d-0e-0f-10-11-12")
329        );
330        assert_eq!(
331            uppercase_hexadecimal.format_buffer(FORMATTING_TEST_VALUES),
332            String::from("0A-0B-0C-0D-0E-0F-10-11-12")
333        );
334        assert_eq!(
335            decimal.format_buffer(FORMATTING_TEST_VALUES),
336            String::from("10-11-12-13-14-15-16-17-18")
337        );
338        assert_eq!(
339            octal.format_buffer(FORMATTING_TEST_VALUES),
340            String::from("012-013-014-015-016-017-020-021-022")
341        );
342        assert_eq!(
343            binary.format_buffer(FORMATTING_TEST_VALUES),
344            String::from(
345                "00001010-00001011-00001100-00001101-00001110-00001111-00010000-00010001-00010010"
346            )
347        );
348    }
349
350    #[test]
351    fn test_owned_separator() {
352        // Test new_owned with runtime strings
353        let runtime_sep = String::from(" | ");
354        let formatter = DecimalFormatter::new_owned(Some(runtime_sep));
355        assert_eq!(
356            formatter.format_buffer(FORMATTING_TEST_VALUES),
357            String::from("10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18")
358        );
359
360        // Test with None (should use default)
361        let formatter = OctalFormatter::new_owned(None);
362        assert_eq!(formatter.get_separator(), ":");
363    }
364
365    #[test]
366    fn test_from_cow() {
367        // Test with borrowed Cow
368        let sep_borrowed: Cow<'static, str> = Cow::Borrowed(" | ");
369        let formatter = DecimalFormatter::from(sep_borrowed);
370        assert_eq!(formatter.get_separator(), " | ");
371
372        // Test with owned Cow
373        let sep_owned: Cow<'static, str> = Cow::Owned(String::from(" | "));
374        let formatter = OctalFormatter::from(sep_owned);
375        assert_eq!(formatter.get_separator(), " | ");
376
377        // Test using .into()
378        let formatter: UppercaseHexadecimalFormatter = Cow::Borrowed("-").into();
379        assert_eq!(
380            formatter.format_buffer(FORMATTING_TEST_VALUES),
381            String::from("0A-0B-0C-0D-0E-0F-10-11-12")
382        );
383
384        // Test all formatters
385        let lowercase_hex: LowercaseHexadecimalFormatter = Cow::Borrowed(" ").into();
386        let binary: BinaryFormatter = Cow::<'static, str>::Owned(String::from(",")).into();
387
388        assert_eq!(lowercase_hex.get_separator(), " ");
389        assert_eq!(binary.get_separator(), ",");
390    }
391
392    #[test]
393    fn test_from_static_str() {
394        // Test From<&str> with string literals
395        let formatter = DecimalFormatter::from("-");
396        assert_eq!(formatter.get_separator(), "-");
397        assert_eq!(
398            formatter.format_buffer(FORMATTING_TEST_VALUES),
399            String::from("10-11-12-13-14-15-16-17-18")
400        );
401
402        // Test using .into()
403        let formatter: OctalFormatter = " | ".into();
404        assert_eq!(formatter.get_separator(), " | ");
405
406        // Test with runtime &str (this is the key improvement)
407        let runtime_string = String::from(" -> ");
408        let runtime_str: &str = runtime_string.as_str();
409        let formatter: UppercaseHexadecimalFormatter = runtime_str.into();
410        assert_eq!(formatter.get_separator(), " -> ");
411        assert_eq!(
412            formatter.format_buffer(FORMATTING_TEST_VALUES),
413            String::from("0A -> 0B -> 0C -> 0D -> 0E -> 0F -> 10 -> 11 -> 12")
414        );
415
416        // Test all formatter types
417        let _decimal: DecimalFormatter = " ".into();
418        let _octal: OctalFormatter = ",".into();
419        let _uppercase_hex: UppercaseHexadecimalFormatter = "-".into();
420        let _lowercase_hex: LowercaseHexadecimalFormatter = "::".into();
421        let _binary: BinaryFormatter = "_".into();
422    }
423
424    #[test]
425    fn test_from_string() {
426        // Test From<String>
427        let separator = String::from(" -> ");
428        let formatter = DecimalFormatter::from(separator);
429        assert_eq!(formatter.get_separator(), " -> ");
430        assert_eq!(
431            formatter.format_buffer(FORMATTING_TEST_VALUES),
432            String::from("10 -> 11 -> 12 -> 13 -> 14 -> 15 -> 16 -> 17 -> 18")
433        );
434
435        // Test using .into()
436        let formatter: UppercaseHexadecimalFormatter = String::from(" ").into();
437        assert_eq!(
438            formatter.format_buffer(FORMATTING_TEST_VALUES),
439            String::from("0A 0B 0C 0D 0E 0F 10 11 12")
440        );
441
442        // Test all formatter types
443        let _decimal: DecimalFormatter = String::from("-").into();
444        let _octal: OctalFormatter = String::from(",").into();
445        let _uppercase_hex: UppercaseHexadecimalFormatter = String::from("::").into();
446        let _lowercase_hex: LowercaseHexadecimalFormatter = String::from(" | ").into();
447        let _binary: BinaryFormatter = String::from("_").into();
448    }
449
450    #[test]
451    fn test_from_option_static_str() {
452        // Test From<Option<&str>> with Some
453        let formatter = DecimalFormatter::from(Some("-"));
454        assert_eq!(formatter.get_separator(), "-");
455
456        // Test From<Option<&str>> with None (should use default)
457        let formatter = OctalFormatter::from(None as Option<&str>);
458        assert_eq!(formatter.get_separator(), ":");
459
460        // Test using .into()
461        let formatter: UppercaseHexadecimalFormatter = Some(" ").into();
462        assert_eq!(formatter.get_separator(), " ");
463
464        let formatter: LowercaseHexadecimalFormatter = (None as Option<&str>).into();
465        assert_eq!(formatter.get_separator(), ":");
466
467        // Test with runtime &str
468        let runtime_string = String::from(" | ");
469        let runtime_str: &str = runtime_string.as_str();
470        let formatter: BinaryFormatter = Some(runtime_str).into();
471        assert_eq!(formatter.get_separator(), " | ");
472
473        // Test all formatter types
474        let _decimal: DecimalFormatter = Some("->").into();
475        let _octal: OctalFormatter = (None as Option<&str>).into();
476        let _uppercase_hex: UppercaseHexadecimalFormatter = Some(",").into();
477        let _lowercase_hex: LowercaseHexadecimalFormatter = Some("::").into();
478        let _binary: BinaryFormatter = (None as Option<&str>).into();
479    }
480
481    #[test]
482    fn test_from_option_string() {
483        // Test From<Option<String>> with Some
484        let separator = Some(String::from(" | "));
485        let formatter = DecimalFormatter::from(separator);
486        assert_eq!(formatter.get_separator(), " | ");
487
488        // Test From<Option<String>> with None (should use default)
489        let formatter = BinaryFormatter::from(None as Option<String>);
490        assert_eq!(formatter.get_separator(), ":");
491
492        // Test using .into()
493        let formatter: UppercaseHexadecimalFormatter = Some(String::from("-")).into();
494        assert_eq!(formatter.get_separator(), "-");
495
496        let formatter: OctalFormatter = (None as Option<String>).into();
497        assert_eq!(formatter.get_separator(), ":");
498
499        // Test all formatter types
500        let _decimal: DecimalFormatter = Some(String::from("::")).into();
501        let _octal: OctalFormatter = (None as Option<String>).into();
502        let _uppercase_hex: UppercaseHexadecimalFormatter = Some(String::from(" ")).into();
503        let _lowercase_hex: LowercaseHexadecimalFormatter = Some(String::from(",")).into();
504        let _binary: BinaryFormatter = (None as Option<String>).into();
505    }
506
507    #[test]
508    fn test_format_byte() {
509        // Test individual byte formatting
510        let decimal = DecimalFormatter::new_default();
511        let octal = OctalFormatter::new_default();
512        let uppercase_hex = UppercaseHexadecimalFormatter::new_default();
513        let lowercase_hex = LowercaseHexadecimalFormatter::new_default();
514        let binary = BinaryFormatter::new_default();
515
516        let byte = 255u8;
517        assert_eq!(decimal.format_byte(&byte), "255");
518        assert_eq!(octal.format_byte(&byte), "377");
519        assert_eq!(uppercase_hex.format_byte(&byte), "FF");
520        assert_eq!(lowercase_hex.format_byte(&byte), "ff");
521        assert_eq!(binary.format_byte(&byte), "11111111");
522
523        let byte = 0u8;
524        assert_eq!(decimal.format_byte(&byte), "0");
525        assert_eq!(octal.format_byte(&byte), "000");
526        assert_eq!(uppercase_hex.format_byte(&byte), "00");
527        assert_eq!(lowercase_hex.format_byte(&byte), "00");
528        assert_eq!(binary.format_byte(&byte), "00000000");
529    }
530
531    #[test]
532    fn test_edge_cases() {
533        let formatter = DecimalFormatter::new_default();
534
535        // Empty buffer
536        assert_eq!(formatter.format_buffer(&[]), "");
537
538        // Single byte
539        assert_eq!(formatter.format_buffer(&[42]), "42");
540
541        // Multi-character separator
542        let formatter = DecimalFormatter::new(Some(" -> "));
543        assert_eq!(formatter.format_buffer(&[1, 2, 3]), "1 -> 2 -> 3");
544
545        // Empty string separator
546        let formatter = DecimalFormatter::new(Some(""));
547        assert_eq!(formatter.format_buffer(&[10, 11, 12]), "101112");
548    }
549
550    #[test]
551    fn test_default_trait() {
552        // Test Default implementation
553        let decimal = DecimalFormatter::default();
554        let octal = OctalFormatter::default();
555        let uppercase_hex = UppercaseHexadecimalFormatter::default();
556        let lowercase_hex = LowercaseHexadecimalFormatter::default();
557        let binary = BinaryFormatter::default();
558
559        // All should use default separator
560        assert_eq!(decimal.get_separator(), ":");
561        assert_eq!(octal.get_separator(), ":");
562        assert_eq!(uppercase_hex.get_separator(), ":");
563        assert_eq!(lowercase_hex.get_separator(), ":");
564        assert_eq!(binary.get_separator(), ":");
565    }
566
567    #[test]
568    fn test_clone() {
569        // Test Clone trait
570        let original = DecimalFormatter::new(Some("-"));
571        let cloned = original.clone();
572
573        assert_eq!(original.get_separator(), cloned.get_separator());
574        assert_eq!(
575            original.format_buffer(&[1, 2, 3]),
576            cloned.format_buffer(&[1, 2, 3])
577        );
578
579        // Verify they're independent (though Cow makes this subtle)
580        let original_result = original.format_buffer(&[10, 20]);
581        let cloned_result = cloned.format_buffer(&[10, 20]);
582        assert_eq!(original_result, cloned_result);
583    }
584
585    fn assert_unpin<T: Unpin>() {}
586
587    #[test]
588    fn test_unpin() {
589        assert_unpin::<BinaryFormatter>();
590        assert_unpin::<DecimalFormatter>();
591        assert_unpin::<LowercaseHexadecimalFormatter>();
592        assert_unpin::<UppercaseHexadecimalFormatter>();
593        assert_unpin::<OctalFormatter>();
594    }
595
596    #[test]
597    fn test_trait_object_safety() {
598        // Assert trait object construction.
599        let lowercase_hexadecimal: Box<dyn BufferFormatter> =
600            Box::new(LowercaseHexadecimalFormatter::new(None));
601        let uppercase_hexadecimal: Box<dyn BufferFormatter> =
602            Box::new(UppercaseHexadecimalFormatter::new(None));
603        let decimal: Box<dyn BufferFormatter> = Box::new(DecimalFormatter::new(None));
604        let octal: Box<dyn BufferFormatter> = Box::new(OctalFormatter::new(None));
605        let binary: Box<dyn BufferFormatter> = Box::new(BinaryFormatter::new(None));
606
607        // Assert that trait object methods are dispatchable.
608        assert_eq!(lowercase_hexadecimal.get_separator(), ":");
609        assert!(!lowercase_hexadecimal.format_buffer(b"test").is_empty());
610
611        assert_eq!(uppercase_hexadecimal.get_separator(), ":");
612        assert!(!uppercase_hexadecimal.format_buffer(b"test").is_empty());
613
614        assert_eq!(decimal.get_separator(), ":");
615        assert!(!decimal.format_buffer(b"test").is_empty());
616
617        assert_eq!(octal.get_separator(), ":");
618        assert!(!octal.format_buffer(b"test").is_empty());
619
620        assert_eq!(binary.get_separator(), ":");
621        assert!(!binary.format_buffer(b"test").is_empty());
622    }
623
624    fn assert_buffer_formatter<T: BufferFormatter>() {}
625
626    #[test]
627    fn test_box() {
628        assert_buffer_formatter::<Box<dyn BufferFormatter>>();
629        assert_buffer_formatter::<Box<LowercaseHexadecimalFormatter>>();
630        assert_buffer_formatter::<Box<UppercaseHexadecimalFormatter>>();
631        assert_buffer_formatter::<Box<DecimalFormatter>>();
632        assert_buffer_formatter::<Box<OctalFormatter>>();
633        assert_buffer_formatter::<Box<BinaryFormatter>>();
634    }
635
636    #[test]
637    fn test_arc() {
638        assert_buffer_formatter::<Arc<LowercaseHexadecimalFormatter>>();
639        assert_buffer_formatter::<Arc<UppercaseHexadecimalFormatter>>();
640        assert_buffer_formatter::<Arc<DecimalFormatter>>();
641        assert_buffer_formatter::<Arc<OctalFormatter>>();
642        assert_buffer_formatter::<Arc<BinaryFormatter>>();
643        assert_buffer_formatter::<Arc<dyn BufferFormatter + Sync>>();
644    }
645
646    fn assert_send<T: Send>() {}
647
648    #[test]
649    fn test_send() {
650        assert_send::<LowercaseHexadecimalFormatter>();
651        assert_send::<UppercaseHexadecimalFormatter>();
652        assert_send::<DecimalFormatter>();
653        assert_send::<OctalFormatter>();
654        assert_send::<BinaryFormatter>();
655
656        assert_send::<Box<dyn BufferFormatter>>();
657        assert_send::<Box<LowercaseHexadecimalFormatter>>();
658        assert_send::<Box<UppercaseHexadecimalFormatter>>();
659        assert_send::<Box<DecimalFormatter>>();
660        assert_send::<Box<OctalFormatter>>();
661        assert_send::<Box<BinaryFormatter>>();
662
663        // Arc wrapper types
664        assert_send::<Arc<LowercaseHexadecimalFormatter>>();
665        assert_send::<Arc<UppercaseHexadecimalFormatter>>();
666        assert_send::<Arc<DecimalFormatter>>();
667        assert_send::<Arc<OctalFormatter>>();
668        assert_send::<Arc<BinaryFormatter>>();
669        assert_send::<Arc<dyn BufferFormatter + Sync>>();
670    }
671
672    #[test]
673    fn test_arc_impl() {
674        // Test Arc<T> implementation
675        let formatter = Arc::new(DecimalFormatter::new(Some(" -> ")));
676        assert_eq!(formatter.get_separator(), " -> ");
677        assert_eq!(
678            formatter.format_buffer(FORMATTING_TEST_VALUES),
679            String::from("10 -> 11 -> 12 -> 13 -> 14 -> 15 -> 16 -> 17 -> 18")
680        );
681
682        // Test Arc can be cloned and shared
683        let formatter_clone = Arc::clone(&formatter);
684        assert_eq!(formatter_clone.format_buffer(&[42, 43]), "42 -> 43");
685
686        // Test all formatter types with Arc
687        let octal = Arc::new(OctalFormatter::new_default());
688        assert_eq!(octal.format_buffer(&[8, 9, 10]), "010:011:012");
689
690        let uppercase_hex = Arc::new(UppercaseHexadecimalFormatter::new(Some(",")));
691        assert_eq!(uppercase_hex.format_buffer(&[255, 254]), "FF,FE");
692
693        let lowercase_hex = Arc::new(LowercaseHexadecimalFormatter::new(Some(" ")));
694        assert_eq!(lowercase_hex.format_buffer(&[0xAB, 0xCD]), "ab cd");
695
696        let binary = Arc::new(BinaryFormatter::new(Some("|")));
697        assert_eq!(binary.format_buffer(&[3, 7]), "00000011|00000111");
698    }
699
700    #[test]
701    fn test_arc_trait_object() {
702        // Test Arc<dyn BufferFormatter + Sync>
703        let formatter: Arc<dyn BufferFormatter + Sync> = Arc::new(DecimalFormatter::new(Some("-")));
704
705        assert_eq!(formatter.get_separator(), "-");
706        assert_eq!(formatter.format_buffer(&[1, 2, 3]), "1-2-3");
707
708        // Test with different formatter types
709        let formatters: Vec<Arc<dyn BufferFormatter + Sync>> = vec![
710            Arc::new(DecimalFormatter::new_default()),
711            Arc::new(OctalFormatter::new_default()),
712            Arc::new(UppercaseHexadecimalFormatter::new_default()),
713            Arc::new(LowercaseHexadecimalFormatter::new_default()),
714            Arc::new(BinaryFormatter::new_default()),
715        ];
716
717        let data = vec![10u8];
718        let results: Vec<String> = formatters.iter().map(|f| f.format_buffer(&data)).collect();
719
720        assert_eq!(results[0], "10"); // Decimal
721        assert_eq!(results[1], "012"); // Octal
722        assert_eq!(results[2], "0A"); // Uppercase Hex
723        assert_eq!(results[3], "0a"); // Lowercase Hex
724        assert_eq!(results[4], "00001010"); // Binary
725
726        // Test Arc trait object can be cloned
727        let formatter_clone = Arc::clone(&formatters[0]);
728        assert_eq!(formatter_clone.format_buffer(&[42]), "42");
729    }
730
731    #[test]
732    fn test_arc_thread_safety() {
733        // Test that Arc<T: BufferFormatter> can be shared across threads
734        let formatter = Arc::new(DecimalFormatter::new(Some(" | ")));
735        let formatter_clone = Arc::clone(&formatter);
736
737        let handle = thread::spawn(move || formatter_clone.format_buffer(&[1, 2, 3, 4, 5]));
738
739        let result = handle.join().unwrap();
740        assert_eq!(result, "1 | 2 | 3 | 4 | 5");
741
742        // Original formatter still works
743        assert_eq!(formatter.format_buffer(&[10, 20]), "10 | 20");
744    }
745
746    #[test]
747    fn test_arc_wrapper_types() {
748        // Test that Arc types work
749        let arc_formatter = Arc::new(OctalFormatter::new(Some("-")));
750        assert_eq!(arc_formatter.format_buffer(&[8, 9]), "010-011");
751
752        // Test Arc trait object
753        let arc_trait: Arc<dyn BufferFormatter + Sync> = Arc::new(DecimalFormatter::new(Some(" ")));
754        assert_eq!(arc_trait.format_buffer(&[1, 2, 3]), "1 2 3");
755    }
756
757    #[test]
758    fn test_partial_eq() {
759        // Test equality with same separator
760        let formatter1 = DecimalFormatter::new(Some("-"));
761        let formatter2 = DecimalFormatter::new(Some("-"));
762        assert_eq!(formatter1, formatter2);
763
764        // Test equality with default separator
765        let formatter3 = DecimalFormatter::new_default();
766        let formatter4 = DecimalFormatter::new(None);
767        assert_eq!(formatter3, formatter4);
768
769        // Test inequality with different separators
770        let formatter5 = DecimalFormatter::new(Some("-"));
771        let formatter6 = DecimalFormatter::new(Some(":"));
772        assert_ne!(formatter5, formatter6);
773
774        // Note: different formatter types cannot be compared in Rust (type mismatch is a compile-time error);
775        // this test only checks that same-type formatters with the same separator are equal.
776        let octal1 = OctalFormatter::new(Some(" "));
777        let octal2 = OctalFormatter::new(Some(" "));
778        assert_eq!(octal1, octal2);
779
780        // Test with static constructor
781        let formatter7 = UppercaseHexadecimalFormatter::new_static(Some("-"));
782        let formatter8 = UppercaseHexadecimalFormatter::new(Some("-"));
783        assert_eq!(formatter7, formatter8);
784
785        // Test with owned constructor
786        let formatter9 = LowercaseHexadecimalFormatter::new_owned(Some(String::from(",")));
787        let formatter10 = LowercaseHexadecimalFormatter::new(Some(","));
788        assert_eq!(formatter9, formatter10);
789    }
790
791    #[test]
792    fn test_hash() {
793        use std::collections::HashMap;
794
795        // Test that formatters with same separator hash to same value
796        let formatter1 = DecimalFormatter::new(Some("-"));
797        let formatter2 = DecimalFormatter::new(Some("-"));
798
799        let mut map = HashMap::new();
800        map.insert(formatter1, "first");
801        map.insert(formatter2, "second"); // Should overwrite "first"
802
803        assert_eq!(map.len(), 1);
804        assert_eq!(map.get(&DecimalFormatter::new(Some("-"))), Some(&"second"));
805
806        // Test using formatters in HashSet
807        use std::collections::HashSet;
808        let mut set = HashSet::new();
809        set.insert(OctalFormatter::new(Some(":")));
810        set.insert(OctalFormatter::new(Some(":")));
811        set.insert(OctalFormatter::new(Some("-")));
812
813        assert_eq!(set.len(), 2); // Two unique separators
814
815        // Test that all formatter types can be hashed (separately)
816        let mut decimal_set = HashSet::new();
817        decimal_set.insert(DecimalFormatter::new_default());
818        decimal_set.insert(DecimalFormatter::new(Some("-")));
819        assert_eq!(decimal_set.len(), 2);
820
821        let mut octal_set = HashSet::new();
822        octal_set.insert(OctalFormatter::new_default());
823        assert_eq!(octal_set.len(), 1);
824
825        let mut upper_hex_set = HashSet::new();
826        upper_hex_set.insert(UppercaseHexadecimalFormatter::new(Some(" ")));
827        assert_eq!(upper_hex_set.len(), 1);
828
829        let mut lower_hex_set = HashSet::new();
830        lower_hex_set.insert(LowercaseHexadecimalFormatter::new(Some(" ")));
831        assert_eq!(lower_hex_set.len(), 1);
832
833        let mut binary_set = HashSet::new();
834        binary_set.insert(BinaryFormatter::new_static(Some(",")));
835        assert_eq!(binary_set.len(), 1);
836    }
837
838    #[test]
839    fn test_display() {
840        // Test Display implementation with regular separators
841        let formatter = DecimalFormatter::new(Some("-"));
842        assert_eq!(
843            format!("{}", formatter),
844            "DecimalFormatter(separator: \"-\")"
845        );
846
847        let formatter = OctalFormatter::new_default();
848        assert_eq!(format!("{}", formatter), "OctalFormatter(separator: \":\")");
849
850        let formatter = UppercaseHexadecimalFormatter::new(Some(" | "));
851        assert_eq!(
852            format!("{}", formatter),
853            "UppercaseHexadecimalFormatter(separator: \" | \")"
854        );
855
856        let formatter = LowercaseHexadecimalFormatter::new(Some(""));
857        assert_eq!(
858            format!("{}", formatter),
859            "LowercaseHexadecimalFormatter(separator: \"\")"
860        );
861
862        let formatter = BinaryFormatter::new_owned(Some(String::from(" -> ")));
863        assert_eq!(
864            format!("{}", formatter),
865            "BinaryFormatter(separator: \" -> \")"
866        );
867
868        // Test that Display output is useful for logging
869        let formatter = DecimalFormatter::new_static(Some("::"));
870        let output = format!("Using formatter: {}", formatter);
871        assert_eq!(
872            output,
873            "Using formatter: DecimalFormatter(separator: \"::\")"
874        );
875    }
876
877    #[test]
878    fn test_display_with_special_characters() {
879        // Test Display implementation properly escapes special characters
880        // This prevents log forging and ensures unambiguous output
881
882        // Test newline character
883        let formatter = DecimalFormatter::new(Some("\n"));
884        assert_eq!(
885            format!("{}", formatter),
886            "DecimalFormatter(separator: \"\\n\")"
887        );
888
889        // Test tab character
890        let formatter = OctalFormatter::new(Some("\t"));
891        assert_eq!(
892            format!("{}", formatter),
893            "OctalFormatter(separator: \"\\t\")"
894        );
895
896        // Test carriage return
897        let formatter = UppercaseHexadecimalFormatter::new(Some("\r"));
898        assert_eq!(
899            format!("{}", formatter),
900            "UppercaseHexadecimalFormatter(separator: \"\\r\")"
901        );
902
903        // Test double quote character
904        let formatter = LowercaseHexadecimalFormatter::new(Some("\""));
905        assert_eq!(
906            format!("{}", formatter),
907            "LowercaseHexadecimalFormatter(separator: \"\\\"\")"
908        );
909
910        // Test backslash character
911        let formatter = BinaryFormatter::new(Some("\\"));
912        assert_eq!(
913            format!("{}", formatter),
914            "BinaryFormatter(separator: \"\\\\\")"
915        );
916
917        // Test multiple special characters combined
918        let formatter = DecimalFormatter::new(Some("\n\t\r\"\\"));
919        assert_eq!(
920            format!("{}", formatter),
921            "DecimalFormatter(separator: \"\\n\\t\\r\\\"\\\\\")"
922        );
923
924        // Test potential log forging attempt
925        let malicious_sep = "\nINFO: Fake log entry";
926        let formatter = OctalFormatter::new(Some(malicious_sep));
927        let output = format!("{}", formatter);
928        // The newline should be escaped, preventing it from creating a new log line
929        assert!(output.contains("\\n"));
930        assert!(!output.contains("\nINFO"));
931        assert_eq!(
932            output,
933            "OctalFormatter(separator: \"\\nINFO: Fake log entry\")"
934        );
935
936        // Test unicode and other edge cases
937        let formatter = UppercaseHexadecimalFormatter::new(Some("→\u{200B}←")); // Arrow with zero-width space
938        let output = format!("{}", formatter);
939        // Should contain the escaped or properly formatted unicode
940        assert!(output.contains("separator:"));
941    }
942}