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}