coap_message_utils/
debug.rs

1//! Helpers and implementations around the public [`show()`] wrapper.
2
3use coap_message::{MessageOption as _, ReadableMessage};
4
5pub struct ShowMessage<'a, M: ReadableMessage>(&'a M);
6
7impl<'a, M: ReadableMessage> core::fmt::Debug for ShowMessage<'a, M> {
8    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9        // not used because unstable / see https://github.com/rust-lang/rust/issues/117729
10        // (and not complete yet for lack of options display)
11        #[cfg(any())] // aka. cfg(false)
12        f.debug_struct("ReadableMessage")
13            .field("type", &core::any::type_name::<M>())
14            .field("code", self.0.code().show_dotted_and_named())
15            .field_with("payload", |f| Ok(()))
16            .finish();
17
18        write!(
19            f,
20            "ReadableMessage {{\n    type: {},\n    code: {:?},\n    options: {{\n        ",
21            core::any::type_name::<M>(),
22            self.0.code().show_dotted_and_named(),
23        )?;
24        for (i, o) in self.0.options().enumerate() {
25            if i != 0 {
26                write!(f, ",\n        ")?;
27            }
28            if let Some(name) = coap_numbers::option::to_name(o.number()) {
29                write!(f, "{} ({}): {:?}", name, o.number(), o.value())?;
30            } else {
31                write!(f, "{}: {:?}", o.number(), o.value())?;
32            }
33        }
34        write!(f, "\n    }},\n    payload: {:?}\n}}", self.0.payload())?;
35
36        Ok(())
37    }
38}
39
40#[cfg(feature = "defmt_0_3")]
41impl<'a, M: ReadableMessage> defmt_0_3::Format for ShowMessage<'a, M> {
42    fn format(&self, f: defmt_0_3::Formatter) {
43        use defmt::write;
44        use defmt_0_3 as defmt;
45
46        write!(
47            f,
48            "ReadableMessage {{\n    type: {=str},\n    code: {},\n    options: {{\n        ",
49            core::any::type_name::<M>(),
50            self.0.code().show_dotted_and_named(),
51        );
52        for (i, o) in self.0.options().enumerate() {
53            if i != 0 {
54                write!(f, ",\n        ");
55            }
56            if let Some(name) = coap_numbers::option::to_name(o.number()) {
57                write!(f, "{} ({}): {:?}", name, o.number(), o.value());
58            } else {
59                write!(f, "{}: {:?}", o.number(), o.value());
60            }
61        }
62        write!(f, "\n    }},\n    payload: {:?}\n}}", self.0.payload());
63    }
64}
65
66/// Extension trait providing utility functions on [coap_message::ReadableMessage].
67pub trait ShowMessageExt: ReadableMessage + Sized {
68    /// Wraps the message to have a [`core::fmt::Debug`] imlementation, and also provide
69    /// [`defmt_0_3::Format`] if the `defmt_0_3` feature is selected.
70    ///
71    /// ```
72    /// # use coap_message::MinimalWritableMessage;
73    /// # let mut message = coap_message_implementations::heap::HeapMessage::new();
74    /// message.set_code(coap_numbers::code::GET);
75    /// message.add_option_str(coap_numbers::option::URI_PATH, "hello");
76    ///
77    /// use coap_message_utils::ShowMessageExt;
78    /// let shown = format!("{:?}", message.show());
79    /// // The precise format is not fixed, but it contains some usable details:
80    /// assert!(shown.contains("code: 0.01 GET"));
81    /// assert!(shown.contains("Uri-Path (11):"));
82    /// ```
83    fn show(&self) -> ShowMessage<'_, Self> {
84        ShowMessage(self)
85    }
86}
87
88impl<M: ReadableMessage> ShowMessageExt for M {}
89
90pub struct ShowDotted(u8);
91
92impl core::fmt::Debug for ShowDotted {
93    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94        coap_numbers::code::format_dotted(self.0, f)
95    }
96}
97
98#[cfg(feature = "defmt_0_3")]
99impl defmt_0_3::Format for ShowDotted {
100    fn format(&self, f: defmt_0_3::Formatter) {
101        use defmt::write;
102        use defmt_0_3 as defmt;
103
104        write!(f, "{=u8}.{=u8:02}", self.0 >> 5, self.0 & 0x1f,)
105    }
106}
107
108pub struct ShowNamed(u8);
109
110impl core::fmt::Debug for ShowNamed {
111    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112        if let Some(name) = coap_numbers::code::to_name(self.0) {
113            write!(f, "{}", name)
114        } else {
115            write!(f, "{:?}", self.0.show_dotted())
116        }
117    }
118}
119
120#[cfg(feature = "defmt_0_3")]
121impl defmt_0_3::Format for ShowNamed {
122    fn format(&self, f: defmt_0_3::Formatter) {
123        use defmt::write;
124        use defmt_0_3 as defmt;
125
126        if let Some(name) = coap_numbers::code::to_name(self.0) {
127            write!(f, "{}", name)
128        } else {
129            write!(f, "{:?}", self.0.show_dotted())
130        }
131    }
132}
133
134pub struct ShowDottedAndNamed(u8);
135
136impl core::fmt::Debug for ShowDottedAndNamed {
137    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138        if let Some(name) = coap_numbers::code::to_name(self.0) {
139            write!(f, "{:?} {}", self.0.show_dotted(), name)
140        } else {
141            write!(f, "{:?}", self.0.show_dotted())
142        }
143    }
144}
145
146#[cfg(feature = "defmt_0_3")]
147impl defmt_0_3::Format for ShowDottedAndNamed {
148    fn format(&self, f: defmt_0_3::Formatter) {
149        use defmt::write;
150        use defmt_0_3 as defmt;
151
152        if let Some(name) = coap_numbers::code::to_name(self.0) {
153            write!(f, "{:?} {=str}", self.0.show_dotted(), name)
154        } else {
155            write!(f, "{:?}", self.0.show_dotted())
156        }
157    }
158}
159
160/// Extension trait providing utility functions on [coap_message::Code].
161pub trait ShowCodeExt: coap_message::Code {
162    /// Wraps the code to have a [`core::fmt::Debug`] imlementation that produces dotted format,
163    /// and also provides [`defmt_0_3::Format`] if the `defmt_0_3` feature is selected.
164    ///
165    /// ```
166    /// use coap_message_utils::ShowCodeExt;
167    /// assert_eq!("2.05", format!("{:?}", coap_numbers::code::CONTENT.show_dotted()));
168    /// ```
169    fn show_dotted(self) -> ShowDotted {
170        let numeric: u8 = self.into();
171        ShowDotted(numeric)
172    }
173
174    /// Wraps the code to have a [`core::fmt::Debug`] imlementation that produces the code's name
175    /// (falling back to dotted format), and also provides [`defmt_0_3::Format`] if the `defmt_0_3`
176    /// feature is selected.
177    ///
178    /// ```
179    /// use coap_message_utils::ShowCodeExt;
180    /// assert_eq!("Content", format!("{:?}", coap_numbers::code::CONTENT.show_named()));
181    /// assert_eq!("0.31", format!("{:?}", 31u8.show_named()));
182    /// ```
183    fn show_named(self) -> ShowNamed {
184        let numeric: u8 = self.into();
185        ShowNamed(numeric)
186    }
187
188    /// Wraps the code to have a [`core::fmt::Debug`] imlementation that produces the code's name
189    /// (falling back to dotted format), and also provides [`defmt_0_3::Format`] if the `defmt_0_3`
190    /// feature is selected.
191    ///
192    /// ```
193    /// use coap_message_utils::ShowCodeExt;
194    /// assert_eq!("2.05 Content", format!("{:?}", coap_numbers::code::CONTENT.show_dotted_and_named()));
195    /// assert_eq!("0.31", format!("{:?}", 31u8.show_dotted_and_named()));
196    /// ```
197    fn show_dotted_and_named(self) -> ShowDottedAndNamed {
198        let numeric: u8 = self.into();
199        ShowDottedAndNamed(numeric)
200    }
201}
202
203impl<C: coap_message::Code> ShowCodeExt for C {}