grovedb_visualize/
lib.rs

1// MIT LICENSE
2//
3// Copyright (c) 2021 Dash Core Group
4//
5// Permission is hereby granted, free of charge, to any
6// person obtaining a copy of this software and associated
7// documentation files (the "Software"), to deal in the
8// Software without restriction, including without
9// limitation the rights to use, copy, modify, merge,
10// publish, distribute, sublicense, and/or sell copies of
11// the Software, and to permit persons to whom the Software
12// is furnished to do so, subject to the following
13// conditions:
14//
15// The above copyright notice and this permission notice
16// shall be included in all copies or substantial portions
17// of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27// DEALINGS IN THE SOFTWARE.
28
29//! Visualize
30
31#![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
42/// Pretty visualization of GroveDB components.
43pub trait Visualize {
44    /// Visualize
45    fn visualize<W: Write>(&self, drawer: Drawer<W>) -> Result<Drawer<W>>;
46}
47
48/// Wrapper struct with a `Debug` implementation to represent bytes vector in
49/// human-friendly way.
50#[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/// Wrapper struct with a `Debug` implementation to represent vector of bytes
63/// vectors in human-friendly way.
64#[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
85/// A `io::Write` proxy to prepend padding and symbols to draw trees
86pub struct Drawer<W: Write> {
87    level: usize,
88    write: W,
89}
90
91impl<W: Write> Drawer<W> {
92    /// New
93    pub fn new(write: W) -> Self {
94        Drawer { level: 0, write }
95    }
96
97    /// Down
98    pub fn down(&mut self) {
99        self.level += 1;
100    }
101
102    /// Up
103    pub fn up(&mut self) {
104        self.level -= 1;
105    }
106
107    /// Write
108    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    /// Flush
125    pub fn flush(&mut self) -> Result<()> {
126        self.write.write_all(b"\n")?;
127        self.write.flush()?;
128        Ok(())
129    }
130}
131
132/// To hex
133pub 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
184/// `visulize` shortcut to write straight into stderr offhand
185pub 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
193/// `visualize` shortcut to write straight into stdout offhand
194pub 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
202/// `visualize` shortcut to write into provided buffer, should be a `Vec` not a
203/// slice because slices won't grow if needed.
204pub 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}