show_bytes/
lib.rs

1//! # show-bytes
2//!
3//! Display bytes as printable ascii with escape sequences as needed.
4//!
5//! ## Examples
6//!
7//! ```rust
8//! use show_bytes::println;
9//!
10//! // byte slice
11//! let bytes_slice: &[u8] = &[72, 101, 108, 108, 111, 0, 255];
12//! println(bytes_slice);
13//!
14//! // byte vector
15//! let bytes_vec: Vec<u8> = vec![72, 101, 108, 108, 111, 0, 255];
16//! println(&bytes_vec);
17//! println(bytes_vec);
18//!
19//! // byte array
20//! let bytes_array: [u8; 7] = [72, 101, 108, 108, 111, 0, 255];
21//! println(bytes_array);
22//! println(&bytes_array);
23//!
24//! // byte iterator
25//! let mut bytes_iter = [72, 101, 108, 108, 111, 0, 255].iter();
26//! println(bytes_iter.clone());
27//! println(&mut bytes_iter);
28//!
29//! // &str
30//! let bytes_str: &str = "hello\0\x7f";
31//! println(bytes_str.bytes());
32//! let bytes_str = &bytes_str;
33//! println(bytes_str.bytes());
34//!
35//! // String
36//! let bytes_string: String = bytes_str.to_string();
37//! println(bytes_string.bytes());
38//! let bytes_string = &bytes_string;
39//! println(bytes_string.bytes());
40//!
41//! // OsString
42//! let bytes_os_string: OsString = OsString::from(bytes_str);
43//! println(bytes_os_string.as_bytes());
44//! let bytes_os_string: &OsString = &bytes_os_string;
45//! println(bytes_os_string.as_bytes());
46//!
47//! // OsStr
48//! let bytes_os_str: &OsStr = OsStr::from_bytes(bytes_slice);
49//! println(bytes_os_str.as_bytes());
50//!
51//! // Box<[u8]>
52//! let boxed_slice: Box<[u8]> = Box::new([72, 101, 108, 108, 111, 0, 255]);
53//! println(boxed_slice.iter());
54//! println(&mut boxed_slice.iter());
55//!
56//! // std::io::Cursor<Vec<u8>>
57//! let cursor = Cursor::new(vec![72, 101, 108, 108, 111, 0, 255]);
58//! let bytes_from_cursor: Vec<u8> = cursor.into_inner();
59//! println(&bytes_from_cursor);
60//! println(bytes_from_cursor);
61//!
62//! // std::collections::VecDeque<u8>
63//! let mut vec_deque = VecDeque::new();
64//! vec_deque.push_back(72);
65//! vec_deque.push_back(101);
66//! vec_deque.push_back(108);
67//! vec_deque.push_back(108);
68//! vec_deque.push_back(111);
69//! vec_deque.push_back(0);
70//! vec_deque.push_back(255);
71//! println(&vec_deque);
72//! println(vec_deque);
73//!
74//! // Cow<[u8]>
75//! let cow_slice: Cow<[u8]> = Cow::Borrowed(&[72, 101, 108, 108, 111, 0, 255]);
76//! println(cow_slice.iter());
77//! let cow_slice: Cow<[u8]> = Cow::Owned(vec![72, 101, 108, 108, 111, 0, 255]);
78//! println(cow_slice.iter());
79//!
80//! // Arc<Vec<u8>>
81//! let arc_slice = Arc::new(vec![72, 101, 108, 108, 111, 0, 255]);
82//! println(arc_slice.iter());
83//!
84//! // Rc<Vec<u8>>
85//! let rc_slice = Rc::new(vec![72, 101, 108, 108, 111, 0, 255]);
86//! println(rc_slice.iter());
87//! ```
88
89use std::borrow::Borrow;
90use std::fmt::{Result, Write};
91
92/// A helper function to show bytes without explicitly creating a Printer struct.
93///
94/// `show_bytes(bytes)` is equivalent to `Printer::new(QuoteStyle::Double).into_string(bytes)`.
95pub fn show_bytes<I>(bytes: I) -> String
96where
97    I: IntoIterator,
98    I::Item: Borrow<u8>,
99{
100    Printer::new(QuoteStyle::Double).into_string(bytes)
101}
102
103/// Maintains an internal state describing how to display a byte array.
104///
105/// It can write to an arbitrary `std::fmt::Write` implementation using the
106/// method `Printer::write_to`.
107#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
108pub struct Printer {
109    quote_style: QuoteStyle,
110}
111
112/// Indicates how, if at all, the printer should quote the byte string.
113///
114/// A printer's quoting style is chosen by passing a `QuoteStyle` to `Printer::new`.
115/// The choice of quoting style affects which characters are escaped.
116#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
117pub enum QuoteStyle {
118    /// Indicates that the printer shouldn't quote its output.
119    None,
120    /// Indicates that the printer should wrap its output in single quotes, and
121    /// escape any single quotes within the output.
122    Single,
123    /// Indicates that the printer should wrap its output in double quotes, and
124    /// escape anydouble quotes within the output.
125    Double,
126}
127
128/// Returns `Quotes::None`.
129impl Default for QuoteStyle {
130    fn default() -> Self {
131        Self::None
132    }
133}
134
135/// Returns a printer that doesn't quote its output.
136///
137/// This is equivalent to `Printer::new(QuoteStyle::None)`.
138impl Default for Printer {
139    fn default() -> Self {
140        Self {
141            quote_style: QuoteStyle::None,
142        }
143    }
144}
145
146impl Printer {
147    /// Returns a new Printer with the chosen quoting style.
148    pub fn new(quote_style: QuoteStyle) -> Self {
149        Self { quote_style }
150    }
151
152    /// Writes the formatted bytes to an arbitrary `std::fmt::Write` implementation.
153    ///
154    /// This method iterates through the byte array and calls `writer.write_char`
155    /// or `writer.write_str` as appropriate. The performance of this method thus
156    /// depends on the implementation of the underlying writer. If a writer doesn't
157    /// implement good buffering, or if each call to `write` makes a system call
158    /// to do IO, this can be inefficient.
159    ///
160    /// The helper method `Printer::into_string` is a thin wrapper around this
161    /// method, calling `write_to` with a `String` as its writer. `String` implements
162    /// `fmt::Write` with methods like `String::push` and `String::push_str`, which
163    /// should be adequately performant for most uses. If the use case calls for it,
164    /// the user can always implement their own writer with custom buffering.
165    pub fn write_to<I, W>(&self, bytes: I, writer: &mut W) -> Result
166    where
167        I: IntoIterator,
168        I::Item: Borrow<u8>,
169        W: Write,
170    {
171        match self.quote_style {
172            QuoteStyle::None => Ok(()),
173            QuoteStyle::Single => writer.write_char('\''),
174            QuoteStyle::Double => writer.write_char('"'),
175        }?;
176
177        for byte_borrow in bytes.into_iter() {
178            let byte = *byte_borrow.borrow();
179            match self.quote_style {
180                QuoteStyle::Single if byte == b'\'' => writer.write_str("\\'"),
181                QuoteStyle::Double if byte == b'"' => writer.write_str("\\\""),
182                _ if byte == b'\\' => writer.write_str("\\\\"),
183                _ if byte.is_ascii_graphic() => writer.write_char(byte as char),
184                _ => write!(writer, "\\x{:02x}", byte),
185            }?;
186        }
187
188        match self.quote_style {
189            QuoteStyle::None => Ok(()),
190            QuoteStyle::Single => writer.write_char('\''),
191            QuoteStyle::Double => writer.write_char('"'),
192        }?;
193
194        Ok(())
195    }
196
197    /// Returns a string displaying the bytes.
198    pub fn into_string<I>(&self, bytes: I) -> String
199    where
200        I: IntoIterator,
201        I::Item: Borrow<u8>,
202    {
203        let mut output = String::new();
204
205        self.write_to(bytes, &mut output).expect(
206            "Writing to a string shouldn't fail, it uses infallible methods like String::push.",
207        );
208
209        output
210    }
211}