smoltcp/wire/
pretty_print.rs

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