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