1 /*! Pretty-printing of packet representation.
2 
3 The `pretty_print` module provides bits and pieces for printing concise,
4 easily human readable packet listings.
5 
6 # Example
7 
8 A packet can be formatted using the `PrettyPrinter` wrapper:
9 
10 ```rust
11 use smoltcp::wire::*;
12 let 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 
29 let result = "\
30 EthernetII 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"))]
36 assert_eq!(
37     result,
38     &format!("{}", PrettyPrinter::<EthernetFrame<&'static [u8]>>::new("", &buffer))
39 );
40 ```
41 */
42 
43 use core::fmt;
44 use core::marker::PhantomData;
45 
46 /// Indentation state.
47 #[derive(Debug)]
48 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
49 pub struct PrettyIndent {
50     prefix: &'static str,
51     level: usize,
52 }
53 
54 impl 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.
new(prefix: &'static str) -> PrettyIndent57     pub fn new(prefix: &'static str) -> PrettyIndent {
58         PrettyIndent { prefix, level: 0 }
59     }
60 
61     /// Increase indentation level.
increase(&mut self, f: &mut fmt::Formatter) -> fmt::Result62     pub fn increase(&mut self, f: &mut fmt::Formatter) -> fmt::Result {
63         writeln!(f)?;
64         self.level += 1;
65         Ok(())
66     }
67 }
68 
69 impl fmt::Display for PrettyIndent {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result70     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.
80 pub 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.
pretty_print( buffer: &dyn AsRef<[u8]>, fmt: &mut fmt::Formatter, indent: &mut PrettyIndent, ) -> fmt::Result86     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.
94 pub struct PrettyPrinter<'a, T: PrettyPrint> {
95     prefix: &'static str,
96     buffer: &'a dyn AsRef<[u8]>,
97     phantom: PhantomData<T>,
98 }
99 
100 impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
101     /// Format the listing with the recorded parameters when Display::fmt is called.
new(prefix: &'static str, buffer: &'a dyn AsRef<[u8]>) -> PrettyPrinter<'a, T>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 
111 impl<'a, T: PrettyPrint + AsRef<[u8]>> PrettyPrinter<'a, T> {
112     /// Create a `PrettyPrinter` which prints the given object.
print(printable: &'a T) -> PrettyPrinter<'a, T>113     pub fn print(printable: &'a T) -> PrettyPrinter<'a, T> {
114         PrettyPrinter {
115             prefix: "",
116             buffer: printable,
117             phantom: PhantomData,
118         }
119     }
120 }
121 
122 impl<'a, T: PrettyPrint> fmt::Display for PrettyPrinter<'a, T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result123     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124         T::pretty_print(&self.buffer, f, &mut PrettyIndent::new(self.prefix))
125     }
126 }
127