sem_reg/data_conversion/
hex_bytes.rs

1use std::fmt::{self};
2
3pub struct HexBytes<'a> {
4    bytes: &'a [u8],
5    old_bytes: Option<&'a [u8]>,
6}
7
8impl<'a> HexBytes<'a> {
9    pub fn new(bytes: &'a [u8]) -> Self {
10        Self {
11            bytes,
12            old_bytes: None,
13        }
14    }
15
16    pub fn diff_against(mut self, old_bytes: &'a [u8]) -> Self {
17        //! This will introduce ANSI escape sequences in the output to color it.
18
19        self.old_bytes = Some(old_bytes);
20        self
21    }
22}
23
24impl fmt::Display for HexBytes<'_> {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        let write_byte = |f: &mut fmt::Formatter<'_>, first: bool, byte| -> fmt::Result {
27            if !first {
28                write!(f, " ")?;
29            }
30            write!(f, "{byte:02x}")?;
31            Ok(())
32        };
33
34        let mut first = true;
35        if let Some(other_bytes) = self.old_bytes {
36            let mut current_color = Color::Default;
37            write!(f, "{current_color}")?;
38
39            for fragment in diff::slice(other_bytes, self.bytes) {
40                let (byte, byte_color) = match fragment {
41                    diff::Result::Left(byte) => (byte, Color::BrightRed),
42                    diff::Result::Right(byte) => (byte, Color::BrightGreen),
43                    diff::Result::Both(byte, _) => (byte, Color::Default),
44                };
45
46                if byte_color != current_color {
47                    write!(f, "{byte_color}")?;
48                    current_color = byte_color;
49                }
50
51                write_byte(f, first, byte)?;
52
53                first = false;
54            }
55
56            if current_color != Color::Default {
57                write!(f, "{}", Color::Default)?;
58            }
59        } else {
60            for byte in self.bytes {
61                write_byte(f, first, byte)?;
62                first = false;
63            }
64        }
65
66        Ok(())
67    }
68}
69
70#[derive(Clone, Copy, PartialEq)]
71enum Color {
72    Default,
73    BrightRed,
74    BrightGreen,
75}
76
77impl fmt::Display for Color {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(
80            f,
81            "{}",
82            match self {
83                // See <https://chrisyeh96.github.io/2020/03/28/terminal-colors.html>.
84                Color::Default => "\x1b[0m",
85                Color::BrightRed => "\x1b[91m",
86                Color::BrightGreen => "\x1b[92m",
87            }
88        )?;
89
90        Ok(())
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::data_conversion::hex_bytes::HexBytes;
97
98    #[test]
99    fn diff_output() {
100        assert_eq!(
101            HexBytes::new(&[0x10, 0xf1, 0xf2, 0x13])
102                .diff_against(&[0x10, 0x11, 0x12, 0x13])
103                .to_string(),
104            String::new()
105                + "\x1b[0m"
106                + "10"
107                + "\x1b[91m"
108                + " 11 12"
109                + "\x1b[92m"
110                + " f1 f2"
111                + "\x1b[0m"
112                + " 13"
113        );
114    }
115}