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