smoltcp/wire/pretty_print.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
/*! Pretty-printing of packet representation.
The `pretty_print` module provides bits and pieces for printing concise,
easily human readable packet listings.
# Example
A packet can be formatted using the `PrettyPrinter` wrapper:
```rust
use smoltcp::wire::*;
let buffer = vec![
// Ethernet II
0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x08, 0x00,
// IPv4
0x45, 0x00, 0x00, 0x20,
0x00, 0x00, 0x40, 0x00,
0x40, 0x01, 0xd2, 0x79,
0x11, 0x12, 0x13, 0x14,
0x21, 0x22, 0x23, 0x24,
// ICMPv4
0x08, 0x00, 0x8e, 0xfe,
0x12, 0x34, 0xab, 0xcd,
0xaa, 0x00, 0x00, 0xff
];
let result = "\
EthernetII src=11-12-13-14-15-16 dst=01-02-03-04-05-06 type=IPv4\n\
\\ IPv4 src=17.18.19.20 dst=33.34.35.36 proto=ICMP (checksum incorrect)\n \
\\ ICMPv4 echo request id=4660 seq=43981 len=4\
";
#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
assert_eq!(
result,
&format!("{}", PrettyPrinter::<EthernetFrame<&'static [u8]>>::new("", &buffer))
);
```
*/
use core::fmt;
use core::marker::PhantomData;
/// Indentation state.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PrettyIndent {
prefix: &'static str,
level: usize,
}
impl PrettyIndent {
/// Create an indentation state. The entire listing will be indented by the width
/// of `prefix`, and `prefix` will appear at the start of the first line.
pub fn new(prefix: &'static str) -> PrettyIndent {
PrettyIndent { prefix, level: 0 }
}
/// Increase indentation level.
pub fn increase(&mut self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f)?;
self.level += 1;
Ok(())
}
}
impl fmt::Display for PrettyIndent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.level == 0 {
write!(f, "{}", self.prefix)
} else {
write!(f, "{0:1$}{0:2$}\\ ", "", self.prefix.len(), self.level - 1)
}
}
}
/// Interface for printing listings.
pub trait PrettyPrint {
/// Write a concise, formatted representation of a packet contained in the provided
/// buffer, and any nested packets it may contain.
///
/// `pretty_print` accepts a buffer and not a packet wrapper because the packet might
/// be truncated, and so it might not be possible to create the packet wrapper.
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
fmt: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result;
}
/// Wrapper for using a `PrettyPrint` where a `Display` is expected.
pub struct PrettyPrinter<'a, T: PrettyPrint> {
prefix: &'static str,
buffer: &'a dyn AsRef<[u8]>,
phantom: PhantomData<T>,
}
impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
/// Format the listing with the recorded parameters when Display::fmt is called.
pub fn new(prefix: &'static str, buffer: &'a dyn AsRef<[u8]>) -> PrettyPrinter<'a, T> {
PrettyPrinter {
prefix: prefix,
buffer: buffer,
phantom: PhantomData,
}
}
}
impl<'a, T: PrettyPrint + AsRef<[u8]>> PrettyPrinter<'a, T> {
/// Create a `PrettyPrinter` which prints the given object.
pub fn print(printable: &'a T) -> PrettyPrinter<'a, T> {
PrettyPrinter {
prefix: "",
buffer: printable,
phantom: PhantomData,
}
}
}
impl<'a, T: PrettyPrint> fmt::Display for PrettyPrinter<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
T::pretty_print(&self.buffer, f, &mut PrettyIndent::new(self.prefix))
}
}