1#![deny(missing_docs)]
32
33use core::fmt;
34use std::io::{Result, Write};
35
36use itertools::Itertools;
37
38static HEX_LEN: usize = 8;
39static STR_LEN: usize = 32;
40static INDENT_SPACES: usize = 4;
41
42pub trait Visualize {
44 fn visualize<W: Write>(&self, drawer: Drawer<W>) -> Result<Drawer<W>>;
46}
47
48#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
51pub struct DebugBytes(pub Vec<u8>);
52
53impl fmt::Debug for DebugBytes {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 let mut v = Vec::new();
56 visualize_to_vec(&mut v, self.0.as_slice());
57
58 f.write_str(&String::from_utf8_lossy(&v))
59 }
60}
61
62#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
65pub struct DebugByteVectors(pub Vec<Vec<u8>>);
66
67impl fmt::Debug for DebugByteVectors {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 let mut v = Vec::new();
70 let mut drawer = Drawer::new(&mut v);
71
72 drawer.write(b"[ ").expect("write to a vector");
73
74 for v in self.0.iter() {
75 drawer = v.visualize(drawer).expect("write to a vector");
76 drawer.write(b", ").expect("write to a vector");
77 }
78
79 drawer.write(b" ]").expect("write to a vector");
80
81 f.write_str(&String::from_utf8_lossy(&v))
82 }
83}
84
85pub struct Drawer<W: Write> {
87 level: usize,
88 write: W,
89}
90
91impl<W: Write> Drawer<W> {
92 pub fn new(write: W) -> Self {
94 Drawer { level: 0, write }
95 }
96
97 pub fn down(&mut self) {
99 self.level += 1;
100 }
101
102 pub fn up(&mut self) {
104 self.level -= 1;
105 }
106
107 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
109 let lines_iter = buf.split(|c| *c == b'\n');
110 let sep = if self.level > 0 {
111 let mut result = " ".repeat(INDENT_SPACES * self.level - 1);
112 result.insert(0, '\n');
113 result
114 } else {
115 String::new()
116 };
117 let interspersed_lines_iter = Itertools::intersperse(lines_iter, sep.as_bytes());
118 for line in interspersed_lines_iter {
119 self.write.write_all(line)?;
120 }
121 Ok(())
122 }
123
124 pub fn flush(&mut self) -> Result<()> {
126 self.write.write_all(b"\n")?;
127 self.write.flush()?;
128 Ok(())
129 }
130}
131
132pub fn to_hex(bytes: &[u8]) -> String {
134 let encoded = hex::encode(bytes);
135 let remaining = encoded.len().saturating_sub(HEX_LEN);
136 if remaining >= 8 {
137 format!("{}..{}", &encoded[0..HEX_LEN], &encoded[remaining..])
138 } else {
139 encoded
140 }
141}
142
143impl Visualize for [u8] {
144 fn visualize<'a, W: Write>(&self, mut drawer: Drawer<W>) -> Result<Drawer<W>> {
145 let hex_repr = to_hex(self);
146 let str_repr = String::from_utf8(self.to_vec());
147 drawer.write(format!("[hex: {hex_repr}").as_bytes())?;
148 if let Ok(str_repr) = str_repr {
149 let str_part = if str_repr.len() > STR_LEN {
150 &str_repr[..=STR_LEN]
151 } else {
152 &str_repr
153 };
154 drawer.write(format!(", str: {str_part}").as_bytes())?;
155 }
156 drawer.write(b"]")?;
157 Ok(drawer)
158 }
159}
160
161impl Visualize for Vec<u8> {
162 fn visualize<W: Write>(&self, drawer: Drawer<W>) -> Result<Drawer<W>> {
163 self.as_slice().visualize(drawer)
164 }
165}
166
167impl<T: Visualize + ?Sized> Visualize for &T {
168 fn visualize<'a, W: Write>(&self, drawer: Drawer<W>) -> Result<Drawer<W>> {
169 (*self).visualize(drawer)
170 }
171}
172
173impl<T: Visualize> Visualize for Option<T> {
174 fn visualize<'a, W: Write>(&self, mut drawer: Drawer<W>) -> Result<Drawer<W>> {
175 Ok(if let Some(v) = self {
176 v.visualize(drawer)?
177 } else {
178 drawer.write(b"None")?;
179 drawer
180 })
181 }
182}
183
184pub fn visualize_stderr<T: Visualize + ?Sized>(value: &T) {
186 let mut out = std::io::stderr();
187 let drawer = Drawer::new(&mut out);
188 value
189 .visualize(drawer)
190 .expect("IO error when trying to `visualize`");
191}
192
193pub fn visualize_stdout<T: Visualize + ?Sized>(value: &T) {
195 let mut out = std::io::stdout();
196 let drawer = Drawer::new(&mut out);
197 value
198 .visualize(drawer)
199 .expect("IO error when trying to `visualize`");
200}
201
202pub fn visualize_to_vec<T: Visualize + ?Sized>(v: &mut Vec<u8>, value: &T) {
205 let drawer = Drawer::new(v);
206 value
207 .visualize(drawer)
208 .expect("error while writing into slice");
209}