feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
#[cfg(all(feature = "encoding-raw", feature = "encoding-rzcobs"))]
|
||||
compile_error!("Multiple `encoding-*` features are enabled. You may only enable one.");
|
||||
|
||||
#[cfg_attr(feature = "encoding-raw", path = "raw.rs")]
|
||||
#[cfg_attr(not(feature = "encoding-raw"), path = "rzcobs.rs")]
|
||||
mod inner;
|
||||
|
||||
// This wrapper struct is to avoid copypasting the public docs in all the impls.
|
||||
|
||||
/// Encode raw defmt frames for sending over the wire.
|
||||
///
|
||||
/// defmt emits "log frames", which are sequences of bytes. The raw log frame data
|
||||
/// is then *encoded* prior to sending over the wire.
|
||||
///
|
||||
/// `Encoder` will encode the frames according to the currently selected
|
||||
/// `encoding-*` Cargo feature. See `Cargo.toml` for the supported encodings
|
||||
/// and their tradeoffs.
|
||||
///
|
||||
/// Encodings may perform two functions:
|
||||
///
|
||||
/// - Framing: Adds extra data to allow the encoder to know when each frame starts
|
||||
/// and ends in the stream. Unframed log frames already contain enough information for
|
||||
/// the decoder to know when they end, so framing is optional. However, without framing
|
||||
/// the decoder must receive all bytes intact or it may "lose sync". With framing, it can
|
||||
/// recover from missing/corrupted data, and can start decoding from the "middle" of an
|
||||
/// already-running stream.
|
||||
/// - Compression: The frame data has rather low entropy (for example, it contains many
|
||||
/// zero bytes due to encoding all integers in fixed with, and will likely contain many
|
||||
/// repetitions). Compression can decrease the on-the-wire required bandwidth.
|
||||
///
|
||||
/// defmt provides the `Encoder` separately instead of feeding already-encoded bytes
|
||||
/// to the `Logger` because `Logger` implementations may decide to allow
|
||||
/// concurrent logging from multiple "contexts" such as threads or interrupt
|
||||
/// priority levels. In this case, the Logger implementation needs to create one
|
||||
/// Encoder for each such context.
|
||||
pub struct Encoder {
|
||||
inner: inner::Encoder,
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
/// Create a new `Encoder`.
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: inner::Encoder::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Start encoding a log frame.
|
||||
///
|
||||
/// `Logger` impls will typically call this from `acquire()`.
|
||||
///
|
||||
/// You may only call `start_frame` when no frame is currently being encoded.
|
||||
/// Failure to do so may result in corrupted data on the wire.
|
||||
///
|
||||
/// The `write` closure will be called with the encoded data that must
|
||||
/// be sent on the wire. It may be called zero, one, or multiple times.
|
||||
pub fn start_frame(&mut self, write: impl FnMut(&[u8])) {
|
||||
self.inner.start_frame(write)
|
||||
}
|
||||
|
||||
/// Finish encoding a log frame.
|
||||
///
|
||||
/// `Logger` impls will typically call this from `release()`.
|
||||
///
|
||||
/// You may only call `end_frame` when a frame is currently being encoded.
|
||||
/// Failure to do so may result in corrupted data on the wire.
|
||||
///
|
||||
/// The `write` closure will be called with the encoded data that must
|
||||
/// be sent on the wire. It may be called zero, one, or multiple times.
|
||||
pub fn end_frame(&mut self, write: impl FnMut(&[u8])) {
|
||||
self.inner.end_frame(write)
|
||||
}
|
||||
|
||||
/// Write part of data for a log frame.
|
||||
///
|
||||
/// `Logger` impls will typically call this from `write()`.
|
||||
///
|
||||
/// You may only call `write` when a frame is currently being encoded.
|
||||
/// Failure to do so may result in corrupted data on the wire.
|
||||
///
|
||||
/// The `write` closure will be called with the encoded data that must
|
||||
/// be sent on the wire. It may be called zero, one, or multiple times.
|
||||
pub fn write(&mut self, data: &[u8], write: impl FnMut(&[u8])) {
|
||||
self.inner.write(data, write)
|
||||
}
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
pub(crate) struct Encoder {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
pub(crate) fn start_frame(&mut self, _write: impl FnMut(&[u8])) {}
|
||||
|
||||
pub(crate) fn end_frame(&mut self, _write: impl FnMut(&[u8])) {}
|
||||
|
||||
pub(crate) fn write(&mut self, data: &[u8], mut write: impl FnMut(&[u8])) {
|
||||
write(data)
|
||||
}
|
||||
}
|
||||
Vendored
+267
@@ -0,0 +1,267 @@
|
||||
// Standard COBS/rCOBS:
|
||||
// 00000000 => end of frame
|
||||
// nnnnnnnn => output n-1 bytes from stream, output 0x00
|
||||
// 11111111 => output 254 bytes from stream
|
||||
//
|
||||
// zCOBS/rzCOBS:
|
||||
// 00000000 => end of frame
|
||||
// 0xxxxxxx => foreach x from LSB to MSB: if x=0 output 1 byte from stream, if x=1 output 0x00
|
||||
// 1nnnnnnn => output n+7 bytes from stream, output 0x00
|
||||
// 11111111 => output 134 bytes from stream
|
||||
|
||||
pub(crate) struct Encoder {
|
||||
run: u8,
|
||||
zeros: u8,
|
||||
started: bool,
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
run: 0,
|
||||
zeros: 0,
|
||||
started: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_frame(&mut self, mut write: impl FnMut(&[u8])) {
|
||||
let mut write_byte = move |b: u8| write(&[b]);
|
||||
if !self.started {
|
||||
self.started = true;
|
||||
|
||||
// Write a frame-separator at the very beginning. This allows the
|
||||
// decoder to correctly decode the first frame if a previous boot had left a
|
||||
// partly-written frame.
|
||||
write_byte(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self, mut write: impl FnMut(&[u8])) {
|
||||
let mut write_byte = move |b: u8| write(&[b]);
|
||||
|
||||
// Finish writing the previous symbol if needed.
|
||||
match self.run {
|
||||
0 => {}
|
||||
1..=6 => write_byte((self.zeros | (0xFF << self.run)) & 0x7F),
|
||||
_ => write_byte((self.run - 7) | 0x80),
|
||||
}
|
||||
|
||||
// Write frame-separator.
|
||||
write_byte(0x00);
|
||||
|
||||
self.run = 0;
|
||||
self.zeros = 0;
|
||||
}
|
||||
|
||||
pub fn write(&mut self, data: &[u8], mut write: impl FnMut(&[u8])) {
|
||||
let mut write_byte = move |b: u8| write(&[b]);
|
||||
|
||||
for &byte in data {
|
||||
if self.run < 7 {
|
||||
if byte == 0 {
|
||||
self.zeros |= 1 << self.run;
|
||||
} else {
|
||||
write_byte(byte);
|
||||
}
|
||||
|
||||
self.run += 1;
|
||||
if self.run == 7 && self.zeros != 0x00 {
|
||||
write_byte(self.zeros);
|
||||
self.run = 0;
|
||||
self.zeros = 0;
|
||||
}
|
||||
} else if byte == 0 {
|
||||
write_byte((self.run - 7) | 0x80);
|
||||
self.run = 0;
|
||||
self.zeros = 0;
|
||||
} else {
|
||||
write_byte(byte);
|
||||
self.run += 1;
|
||||
if self.run == 134 {
|
||||
write_byte(0xFF);
|
||||
self.run = 0;
|
||||
self.zeros = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-test")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let tests: &[(&[u8], &[u8])] = &[
|
||||
(&[], &[0x00]),
|
||||
(&[0x00], &[0x7f, 0x00]),
|
||||
(&[0x00, 0x00], &[0x7f, 0x00]),
|
||||
(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], &[0x7f, 0x00]),
|
||||
(
|
||||
&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
&[0x7f, 0x7f, 0x00],
|
||||
),
|
||||
(&[0x01], &[0x01, 0x7e, 0x00]),
|
||||
(&[0x01, 0x00], &[0x01, 0x7e, 0x00]),
|
||||
(&[0x00, 0x01], &[0x01, 0x7d, 0x00]),
|
||||
(&[0x01, 0x02], &[0x01, 0x02, 0x7c, 0x00]),
|
||||
(
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00],
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x40, 0x00],
|
||||
),
|
||||
(
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77],
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x80, 0x00],
|
||||
),
|
||||
(
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00],
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x80, 0x00],
|
||||
),
|
||||
(
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
|
||||
&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x81, 0x00],
|
||||
),
|
||||
(
|
||||
&[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff,
|
||||
],
|
||||
&[0x7f, 0xff, 0x3f, 0x00],
|
||||
),
|
||||
(
|
||||
&[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff,
|
||||
],
|
||||
&[0x44, 0x5f, 0xff, 0x3f, 0x00],
|
||||
),
|
||||
(
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85,
|
||||
],
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0xfe, 0x00,
|
||||
],
|
||||
),
|
||||
(
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0x00,
|
||||
],
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0xfe, 0x00,
|
||||
],
|
||||
),
|
||||
(
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0x86,
|
||||
],
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0x86, 0xff, 0x00,
|
||||
],
|
||||
),
|
||||
(
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0x86, 0x00,
|
||||
],
|
||||
&[
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
|
||||
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
|
||||
0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
|
||||
0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
|
||||
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
|
||||
0x83, 0x84, 0x85, 0x86, 0xff, 0x7f, 0x00,
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
for (dec, enc) in tests {
|
||||
let mut res: Vec<u8> = Vec::new();
|
||||
|
||||
let mut e = Encoder::new();
|
||||
e.started = true; // simulate that this is not the first frame.
|
||||
|
||||
e.start_frame(|data| res.extend(data));
|
||||
e.write(dec, |data| res.extend(data));
|
||||
e.end_frame(|data| res.extend(data));
|
||||
|
||||
assert_eq!(enc, &res);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
use super::*;
|
||||
|
||||
macro_rules! write_to_le_bytes {
|
||||
($($s:ident),*) => {
|
||||
$(/// Implementation detail
|
||||
pub fn $s(b: &$s) {
|
||||
write(&b.to_le_bytes())
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
write_to_le_bytes!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
|
||||
|
||||
/// Implementation detail
|
||||
pub fn usize(b: &usize) {
|
||||
write(&(*b as u32).to_le_bytes())
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn isize(b: &isize) {
|
||||
write(&(*b as i32).to_le_bytes())
|
||||
}
|
||||
Vendored
+235
@@ -0,0 +1,235 @@
|
||||
mod integers;
|
||||
mod traits;
|
||||
|
||||
use core::fmt::Write as _;
|
||||
|
||||
use crate::{Format, Formatter, Str};
|
||||
|
||||
pub use self::integers::*;
|
||||
pub use bitflags::bitflags;
|
||||
|
||||
pub trait UnsignedInt {}
|
||||
impl UnsignedInt for u8 {}
|
||||
impl UnsignedInt for u16 {}
|
||||
impl UnsignedInt for u32 {}
|
||||
impl UnsignedInt for u64 {}
|
||||
impl UnsignedInt for u128 {}
|
||||
|
||||
#[cfg(feature = "unstable-test")]
|
||||
thread_local! {
|
||||
static I: core::sync::atomic::AtomicU16 = const { core::sync::atomic::AtomicU16::new(0) };
|
||||
static BYTES: core::cell::RefCell<Vec<u8>> = const { core::cell::RefCell::new(Vec::new()) };
|
||||
}
|
||||
|
||||
/// For testing purposes
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub fn fetch_string_index() -> u16 {
|
||||
I.with(|i| i.load(core::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// For testing purposes
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub fn fetch_add_string_index() -> u16 {
|
||||
I.with(|i| i.fetch_add(1, core::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// Get and clear the logged bytes
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub fn fetch_bytes() -> Vec<u8> {
|
||||
BYTES.with(|b| core::mem::take(&mut *b.borrow_mut()))
|
||||
}
|
||||
|
||||
/// Only to be used by the defmt macros
|
||||
/// Safety: must be paired with a later call to release()
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub unsafe fn acquire() {}
|
||||
|
||||
/// Only to be used by the defmt macros
|
||||
/// Safety: must be paired with a later call to release()
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn acquire() {
|
||||
extern "Rust" {
|
||||
fn _defmt_acquire();
|
||||
}
|
||||
_defmt_acquire()
|
||||
}
|
||||
|
||||
/// Only to be used by the defmt macros
|
||||
/// Safety: must follow an earlier call to acquire()
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub unsafe fn release() {}
|
||||
|
||||
/// Only to be used by the defmt macros
|
||||
/// Safety: must follow an earlier call to acquire()
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn release() {
|
||||
extern "Rust" {
|
||||
fn _defmt_release();
|
||||
}
|
||||
_defmt_release()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub fn write(bytes: &[u8]) {
|
||||
BYTES.with(|b| b.borrow_mut().extend(bytes))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
#[inline(always)]
|
||||
pub fn write(bytes: &[u8]) {
|
||||
extern "Rust" {
|
||||
fn _defmt_write(bytes: &[u8]);
|
||||
}
|
||||
unsafe { _defmt_write(bytes) }
|
||||
}
|
||||
|
||||
/// For testing purposes
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub fn timestamp(_fmt: crate::Formatter<'_>) {}
|
||||
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
#[inline(always)]
|
||||
pub fn timestamp(fmt: crate::Formatter<'_>) {
|
||||
extern "Rust" {
|
||||
fn _defmt_timestamp(_: crate::Formatter<'_>);
|
||||
}
|
||||
unsafe { _defmt_timestamp(fmt) }
|
||||
}
|
||||
|
||||
/// Returns the interned string at `address`.
|
||||
pub fn make_istr(address: u16) -> Str {
|
||||
Str { address }
|
||||
}
|
||||
|
||||
/// Create a Formatter.
|
||||
pub fn make_formatter<'a>() -> Formatter<'a> {
|
||||
Formatter {
|
||||
_phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn truncate<T>(x: impl traits::Truncate<T>) -> T {
|
||||
x.truncate()
|
||||
}
|
||||
|
||||
pub fn into_result<T: traits::IntoResult>(x: T) -> Result<T::Ok, T::Error> {
|
||||
x.into_result()
|
||||
}
|
||||
|
||||
/// For testing purposes
|
||||
#[cfg(feature = "unstable-test")]
|
||||
pub fn panic() -> ! {
|
||||
panic!()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
#[inline(always)]
|
||||
pub fn panic() -> ! {
|
||||
extern "Rust" {
|
||||
fn _defmt_panic() -> !;
|
||||
}
|
||||
unsafe { _defmt_panic() }
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn fmt<T: Format + ?Sized>(f: &T) {
|
||||
istr(&T::_format_tag());
|
||||
f._format_data();
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn fmt_slice<T: Format>(values: &[T]) {
|
||||
usize(&values.len());
|
||||
istr(&T::_format_tag());
|
||||
for value in values {
|
||||
value._format_data();
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn f32(b: &f32) {
|
||||
write(&f32::to_bits(*b).to_le_bytes())
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn f64(b: &f64) {
|
||||
write(&f64::to_bits(*b).to_le_bytes())
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn char(b: &char) {
|
||||
write(&(*b as u32).to_le_bytes())
|
||||
}
|
||||
|
||||
pub fn str(s: &str) {
|
||||
usize(&s.len());
|
||||
write(s.as_bytes());
|
||||
}
|
||||
|
||||
pub fn slice(s: &[u8]) {
|
||||
usize(&s.len());
|
||||
write(s);
|
||||
}
|
||||
|
||||
// NOTE: This is passed `&[u8; N]` – it's just coerced to a slice.
|
||||
pub fn u8_array(a: &[u8]) {
|
||||
write(a);
|
||||
}
|
||||
|
||||
// NOTE: This is passed `&[u8; N]` – it's just coerced to a slice.
|
||||
pub fn fmt_array<T: Format>(a: &[T]) {
|
||||
istr(&T::_format_tag());
|
||||
for value in a {
|
||||
value._format_data();
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn istr(s: &Str) {
|
||||
write(&s.address.to_le_bytes())
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn bool(b: &bool) {
|
||||
u8(&(*b as u8));
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn debug(val: &dyn core::fmt::Debug) {
|
||||
core::write!(FmtWrite, "{val:?}").ok();
|
||||
write(&[0xff]);
|
||||
}
|
||||
|
||||
/// Implementation detail
|
||||
pub fn display(val: &dyn core::fmt::Display) {
|
||||
core::write!(FmtWrite, "{val}").ok();
|
||||
write(&[0xff]);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub unsafe fn acquire_and_header(s: &Str) {
|
||||
acquire();
|
||||
istr(s);
|
||||
timestamp(make_formatter());
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn acquire_header_and_release(s: &Str) {
|
||||
// safety: will be released a few lines further down
|
||||
unsafe { acquire() };
|
||||
istr(s);
|
||||
timestamp(make_formatter());
|
||||
// safety: acquire() was called a few lines above
|
||||
unsafe { release() };
|
||||
}
|
||||
|
||||
struct FmtWrite;
|
||||
|
||||
impl core::fmt::Write for FmtWrite {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
#[allow(unused_imports)]
|
||||
use crate as defmt;
|
||||
use crate::{Format, Formatter, Str};
|
||||
|
||||
pub trait Truncate<U> {
|
||||
fn truncate(self) -> U;
|
||||
}
|
||||
|
||||
macro_rules! impl_truncate {
|
||||
($($from:ty => $into:ty),*) => {
|
||||
$(impl Truncate<$into> for $from {
|
||||
fn truncate(self) -> $into {
|
||||
self as $into
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
// We implement `Truncate<X> for X` so that the macro can unconditionally use it,
|
||||
// even if no truncation is performed.
|
||||
impl_truncate!(
|
||||
u8 => u8,
|
||||
u16 => u8,
|
||||
u32 => u8,
|
||||
u64 => u8,
|
||||
u128 => u8,
|
||||
u16 => u16,
|
||||
u32 => u16,
|
||||
u64 => u16,
|
||||
u128 => u16,
|
||||
u32 => u32,
|
||||
u64 => u32,
|
||||
u128 => u32,
|
||||
u64 => u64,
|
||||
u128 => u64,
|
||||
u128 => u128
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
impl Format for NoneError {
|
||||
fn format(&self, _fmt: Formatter) {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn _format_tag() -> Str {
|
||||
defmt_macros::internp!("Unwrap of a None option value")
|
||||
}
|
||||
|
||||
fn _format_data(&self) {}
|
||||
}
|
||||
|
||||
/// Transform `self` into a `Result`
|
||||
///
|
||||
/// # Call sites
|
||||
/// * [`defmt::unwrap!`]
|
||||
pub trait IntoResult {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> IntoResult for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> IntoResult for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Handle to a defmt logger.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Formatter<'a> {
|
||||
pub(crate) _phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
/// An interned string created via [`intern!`].
|
||||
///
|
||||
/// [`intern!`]: macro.intern.html
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Str {
|
||||
/// 16-bit address
|
||||
pub(crate) address: u16,
|
||||
}
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate as defmt;
|
||||
use crate::{export, Format, Formatter, Str};
|
||||
|
||||
/// An "adapter" type to feed `Debug` values into defmt macros, which expect `defmt::Format` values.
|
||||
///
|
||||
/// This adapter disables compression and uses the `core::fmt` code on-device! You should prefer
|
||||
/// `defmt::Format` over `Debug` whenever possible.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct ExpensiveThing();
|
||||
/// # let expensive_thing = ExpensiveThing();
|
||||
/// #
|
||||
/// defmt::info!("{:?}", defmt::Debug2Format(&expensive_thing));
|
||||
/// // ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
|
||||
/// // must `#[derive(Debug)]`
|
||||
/// ```
|
||||
///
|
||||
/// Note that any provided defmt display hints will be ignored
|
||||
/// because this always uses `{:?}` to format the contained value.
|
||||
pub struct Debug2Format<'a, T: fmt::Debug + ?Sized>(pub &'a T);
|
||||
|
||||
impl<T: fmt::Debug + ?Sized> fmt::Debug for Debug2Format<'_, T> {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + ?Sized> Format for Debug2Format<'_, T> {
|
||||
default_format!();
|
||||
|
||||
fn _format_tag() -> Str {
|
||||
defmt_macros::internp!("{=__internal_Debug}")
|
||||
}
|
||||
|
||||
fn _format_data(&self) {
|
||||
export::debug(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// An "adapter" type to feed `Display` values into defmt macros, which expect `defmt::Format` values.
|
||||
///
|
||||
/// This adapter disables compression and uses the `core::fmt` code on-device! You should prefer
|
||||
/// `defmt::Format` over `Display` whenever possible.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # struct ExpensiveThing();
|
||||
/// #
|
||||
/// # impl core::fmt::Display for ExpensiveThing {
|
||||
/// # fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
/// # write!(f, "{}", "expensive")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # let expensive_thing = ExpensiveThing();
|
||||
/// #
|
||||
/// defmt::info!("{}", defmt::Display2Format(&expensive_thing));
|
||||
/// // ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
|
||||
/// // must implement `fmt::Display`
|
||||
/// ```
|
||||
///
|
||||
/// Note that any provided defmt display hints will be ignored
|
||||
/// because this always uses `{}` to format the contained value.
|
||||
pub struct Display2Format<'a, T: fmt::Display + ?Sized>(pub &'a T);
|
||||
|
||||
impl<T: fmt::Display + ?Sized> fmt::Display for Display2Format<'_, T> {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + ?Sized> Format for Display2Format<'_, T> {
|
||||
default_format!();
|
||||
|
||||
fn _format_tag() -> Str {
|
||||
defmt_macros::internp!("{=__internal_Display}")
|
||||
}
|
||||
|
||||
fn _format_data(&self) {
|
||||
export::display(&self.0);
|
||||
}
|
||||
}
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
use super::*;
|
||||
|
||||
impl<T> Format for alloc::boxed::Box<T>
|
||||
where
|
||||
T: ?Sized + Format,
|
||||
{
|
||||
delegate_format!(T, self, &*self);
|
||||
}
|
||||
|
||||
impl<T> Format for alloc::rc::Rc<T>
|
||||
where
|
||||
T: ?Sized + Format,
|
||||
{
|
||||
delegate_format!(T, self, &*self);
|
||||
}
|
||||
|
||||
#[cfg(not(no_cas))]
|
||||
impl<T> Format for alloc::sync::Arc<T>
|
||||
where
|
||||
T: ?Sized + Format,
|
||||
{
|
||||
delegate_format!(T, self, &*self);
|
||||
}
|
||||
|
||||
impl<T> Format for alloc::vec::Vec<T>
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
delegate_format!([T], self, self.as_slice());
|
||||
}
|
||||
|
||||
impl Format for alloc::string::String {
|
||||
delegate_format!(str, self, self.as_str());
|
||||
}
|
||||
|
||||
impl<'a, T> Format for alloc::borrow::Cow<'a, [T]>
|
||||
where
|
||||
T: 'a + Format,
|
||||
[T]: alloc::borrow::ToOwned<Owned = alloc::vec::Vec<T>>,
|
||||
{
|
||||
delegate_format!([T], self, self.as_ref());
|
||||
}
|
||||
|
||||
impl<'a> Format for alloc::borrow::Cow<'a, str> {
|
||||
delegate_format!(str, self, self.as_ref());
|
||||
}
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
use super::*;
|
||||
use crate::export;
|
||||
|
||||
macro_rules! arrays {
|
||||
( $($len:literal $fmt:literal,)+ ) => {
|
||||
impl<T, const N: usize> Format for [T; N]
|
||||
where
|
||||
T: Format
|
||||
{
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
match N {
|
||||
$(
|
||||
$len => internp!($fmt),
|
||||
)+
|
||||
_ => internp!("{=[?]}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {
|
||||
match N {
|
||||
$( $len )|+ => export::fmt_array(self),
|
||||
_ => export::fmt_slice(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
arrays! {
|
||||
0 "{=[?;0]}",
|
||||
1 "{=[?;1]}",
|
||||
2 "{=[?;2]}",
|
||||
3 "{=[?;3]}",
|
||||
4 "{=[?;4]}",
|
||||
5 "{=[?;5]}",
|
||||
6 "{=[?;6]}",
|
||||
7 "{=[?;7]}",
|
||||
8 "{=[?;8]}",
|
||||
9 "{=[?;9]}",
|
||||
10 "{=[?;10]}",
|
||||
11 "{=[?;11]}",
|
||||
12 "{=[?;12]}",
|
||||
13 "{=[?;13]}",
|
||||
14 "{=[?;14]}",
|
||||
15 "{=[?;15]}",
|
||||
16 "{=[?;16]}",
|
||||
17 "{=[?;17]}",
|
||||
18 "{=[?;18]}",
|
||||
19 "{=[?;19]}",
|
||||
20 "{=[?;20]}",
|
||||
21 "{=[?;21]}",
|
||||
22 "{=[?;22]}",
|
||||
23 "{=[?;23]}",
|
||||
24 "{=[?;24]}",
|
||||
25 "{=[?;25]}",
|
||||
26 "{=[?;26]}",
|
||||
27 "{=[?;27]}",
|
||||
28 "{=[?;28]}",
|
||||
29 "{=[?;29]}",
|
||||
30 "{=[?;30]}",
|
||||
31 "{=[?;31]}",
|
||||
32 "{=[?;32]}",
|
||||
64 "{=[?;64]}",
|
||||
128 "{=[?;128]}",
|
||||
256 "{=[?;256]}",
|
||||
512 "{=[?;512]}",
|
||||
1024 "{=[?;1024]}",
|
||||
2048 "{=[?;2048]}",
|
||||
4096 "{=[?;4096]}",
|
||||
8192 "{=[?;8192]}",
|
||||
16384 "{=[?;16384]}",
|
||||
32768 "{=[?;32768]}",
|
||||
65536 "{=[?;65536]}",
|
||||
131072 "{=[?;131072]}",
|
||||
262144 "{=[?;262144]}",
|
||||
524288 "{=[?;524288]}",
|
||||
1048576 "{=[?;1048576]}",
|
||||
2097152 "{=[?;2097152]}",
|
||||
4194304 "{=[?;4194304]}",
|
||||
8388608 "{=[?;8388608]}",
|
||||
16777216 "{=[?;16777216]}",
|
||||
33554432 "{=[?;33554432]}",
|
||||
67108864 "{=[?;67108864]}",
|
||||
134217728 "{=[?;134217728]}",
|
||||
268435456 "{=[?;268435456]}",
|
||||
536870912 "{=[?;536870912]}",
|
||||
1073741824 "{=[?;1073741824]}",
|
||||
100 "{=[?;100]}",
|
||||
1000 "{=[?;1000]}",
|
||||
10000 "{=[?;10000]}",
|
||||
100000 "{=[?;100000]}",
|
||||
1000000 "{=[?;1000000]}",
|
||||
10000000 "{=[?;10000000]}",
|
||||
100000000 "{=[?;100000000]}",
|
||||
1000000000 "{=[?;1000000000]}",
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use core::alloc;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Format for alloc::Layout {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(
|
||||
fmt,
|
||||
"Layout {{ size: {}, align: {} }}",
|
||||
self.size(),
|
||||
self.align()
|
||||
);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
use core::array;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Format for array::TryFromSliceError {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "TryFromSliceError(())");
|
||||
}
|
||||
}
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
use super::*;
|
||||
|
||||
impl<T> Format for core::cell::Cell<T>
|
||||
where
|
||||
T: Format + Copy,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "Cell {{ value: {=?} }})", self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Format for core::cell::RefCell<T>
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("RefCell {{ value: <borrowed> }}|RefCell {{ value: {=?} }}")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {
|
||||
match self.try_borrow() {
|
||||
Err(_) => export::u8(&0),
|
||||
Ok(x) => {
|
||||
export::u8(&1);
|
||||
export::istr(&T::_format_tag());
|
||||
x._format_data()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for core::cell::BorrowError {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "BorrowError")
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for core::cell::BorrowMutError {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "BorrowMutError")
|
||||
}
|
||||
}
|
||||
Vendored
+120
@@ -0,0 +1,120 @@
|
||||
//! Some of these objects don't expose enough to accurately report their debug state. In this case
|
||||
//! we show as much state as we can. Users can always use `Debug2Format` to get more information,
|
||||
//! at the cost of bringing core::fmt into the firmware and doing the layout work on device.
|
||||
//!
|
||||
//! We generally keep the type parameter trait bounds in case it becomes possible to use this
|
||||
//! later, without making a backwards-incompatible change.
|
||||
|
||||
mod alloc_;
|
||||
mod array;
|
||||
mod cell;
|
||||
#[cfg(feature = "ip_in_core")]
|
||||
mod net;
|
||||
mod num;
|
||||
mod ops;
|
||||
mod panic;
|
||||
mod ptr;
|
||||
mod slice;
|
||||
|
||||
use super::*;
|
||||
use crate::export;
|
||||
|
||||
impl<T> Format for Option<T>
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("None|Some({=?})")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {
|
||||
match self {
|
||||
None => export::u8(&0),
|
||||
Some(x) => {
|
||||
export::u8(&1);
|
||||
export::istr(&T::_format_tag());
|
||||
x._format_data()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Format for Result<T, E>
|
||||
where
|
||||
T: Format,
|
||||
E: Format,
|
||||
{
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("Err({=?})|Ok({=?})")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {
|
||||
match self {
|
||||
Err(e) => {
|
||||
export::u8(&0);
|
||||
export::istr(&E::_format_tag());
|
||||
e._format_data()
|
||||
}
|
||||
Ok(x) => {
|
||||
export::u8(&1);
|
||||
export::istr(&T::_format_tag());
|
||||
x._format_data()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Format for core::marker::PhantomData<T> {
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("PhantomData")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {}
|
||||
}
|
||||
|
||||
impl Format for core::convert::Infallible {
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for core::time::Duration {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(
|
||||
fmt,
|
||||
"Duration {{ secs: {=u64}, nanos: {=u32} }}",
|
||||
self.as_secs(),
|
||||
self.subsec_nanos(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Format for core::iter::Zip<A, B>
|
||||
where
|
||||
A: Format,
|
||||
B: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "Zip(..)")
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
use core::net;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Format for net::AddrParseError {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "AddrParseError(_)");
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for net::Ipv4Addr {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
let [a, b, c, d] = self.octets();
|
||||
crate::write!(fmt, "{}.{}.{}.{}", a, b, c, d);
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for net::SocketAddrV4 {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}:{}", self.ip(), self.port());
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for net::Ipv6Addr {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
let octets: [u8; 16] = self.octets();
|
||||
crate::write!(
|
||||
fmt,
|
||||
"{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}",
|
||||
octets[0],
|
||||
octets[1],
|
||||
octets[2],
|
||||
octets[3],
|
||||
octets[4],
|
||||
octets[5],
|
||||
octets[6],
|
||||
octets[7],
|
||||
octets[8],
|
||||
octets[9],
|
||||
octets[10],
|
||||
octets[11],
|
||||
octets[12],
|
||||
octets[13],
|
||||
octets[14],
|
||||
octets[15]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for net::SocketAddrV6 {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "[{}]:{}", self.ip(), self.port());
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for net::IpAddr {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
match self {
|
||||
net::IpAddr::V4(a) => crate::write!(fmt, "{}", a),
|
||||
net::IpAddr::V6(a) => crate::write!(fmt, "{}", a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for net::SocketAddr {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
match self {
|
||||
net::SocketAddr::V4(a) => crate::write!(fmt, "{}", a),
|
||||
net::SocketAddr::V6(a) => crate::write!(fmt, "{}", a),
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
use core::num;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! non_zero {
|
||||
($type:ty, $hint:literal) => {
|
||||
impl Format for $type {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, $hint, self.get());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
non_zero! {num::NonZeroI8, "{=i8}"}
|
||||
non_zero! {num::NonZeroI16, "{=i16}"}
|
||||
non_zero! {num::NonZeroI32, "{=i32}"}
|
||||
non_zero! {num::NonZeroI64, "{=i64}"}
|
||||
non_zero! {num::NonZeroI128, "{=i128}"}
|
||||
non_zero! {num::NonZeroIsize, "{=isize}"}
|
||||
non_zero! {num::NonZeroU8, "{=u8}"}
|
||||
non_zero! {num::NonZeroU16, "{=u16}"}
|
||||
non_zero! {num::NonZeroU32, "{=u32}"}
|
||||
non_zero! {num::NonZeroU64, "{=u64}"}
|
||||
non_zero! {num::NonZeroU128, "{=u128}"}
|
||||
non_zero! {num::NonZeroUsize, "{=usize}"}
|
||||
|
||||
impl Format for num::TryFromIntError {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "TryFromIntError(())");
|
||||
}
|
||||
}
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
use super::*;
|
||||
|
||||
impl<Idx> Format for core::ops::Range<Idx>
|
||||
where
|
||||
Idx: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}..{}", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Idx> Format for core::ops::RangeFrom<Idx>
|
||||
where
|
||||
Idx: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}..", self.start)
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for core::ops::RangeFull {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "..",)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Idx> Format for core::ops::RangeInclusive<Idx>
|
||||
where
|
||||
Idx: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}..={}", self.start(), self.end())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Idx> Format for core::ops::RangeTo<Idx>
|
||||
where
|
||||
Idx: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "..{}", self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Idx> Format for core::ops::RangeToInclusive<Idx>
|
||||
where
|
||||
Idx: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "..={}", self.end)
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
use core::panic;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Format for panic::PanicInfo<'_> {
|
||||
fn format(&self, f: Formatter) {
|
||||
if let Some(location) = self.location() {
|
||||
crate::write!(f, "panicked at {}", location);
|
||||
} else {
|
||||
crate::write!(f, "panicked");
|
||||
}
|
||||
// TODO: consider supporting self.message() once stabilized, or add a crate feature for
|
||||
// conditional support
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for panic::Location<'_> {
|
||||
fn format(&self, f: Formatter) {
|
||||
crate::write!(
|
||||
f,
|
||||
"{=str}:{=u32}:{=u32}",
|
||||
self.file(),
|
||||
self.line(),
|
||||
self.column()
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
use super::*;
|
||||
|
||||
impl<T> Format for core::ptr::NonNull<T> {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", self.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(c_variadic)]
|
||||
macro_rules! fnptr_format_cvariadic {
|
||||
($($Arg:ident),+) => {
|
||||
impl<Ret, $($Arg),*> Format for extern "C" fn($($Arg),* , ...) -> Ret {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", (*self as usize) as *const ())
|
||||
}
|
||||
}
|
||||
impl<Ret, $($Arg),*> Format for unsafe extern "C" fn($($Arg),* , ...) -> Ret {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", (*self as usize) as *const ())
|
||||
}
|
||||
}
|
||||
};
|
||||
() => {
|
||||
// C variadics require at least one other argument
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! fnptr_format_args {
|
||||
($($Arg:ident),*) => {
|
||||
impl<Ret, $($Arg),*> Format for extern "Rust" fn($($Arg),*) -> Ret {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", (*self as usize) as *const ())
|
||||
}
|
||||
}
|
||||
impl<Ret, $($Arg),*> Format for extern "C" fn($($Arg),*) -> Ret {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", (*self as usize) as *const ())
|
||||
}
|
||||
}
|
||||
impl<Ret, $($Arg),*> Format for unsafe extern "Rust" fn($($Arg),*) -> Ret {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", (*self as usize) as *const ())
|
||||
}
|
||||
}
|
||||
impl<Ret, $($Arg),*> Format for unsafe extern "C" fn($($Arg),*) -> Ret {
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "{}", (*self as usize) as *const ())
|
||||
}
|
||||
}
|
||||
#[cfg(c_variadic)]
|
||||
fnptr_format_cvariadic!{ $($Arg),* }
|
||||
};
|
||||
}
|
||||
|
||||
// core::ptr has fnptr impls up to 12 arguments
|
||||
// https://doc.rust-lang.org/src/core/ptr/mod.rs.html#1994
|
||||
fnptr_format_args! {}
|
||||
fnptr_format_args! { A }
|
||||
fnptr_format_args! { A, B }
|
||||
fnptr_format_args! { A, B, C }
|
||||
fnptr_format_args! { A, B, C, D }
|
||||
fnptr_format_args! { A, B, C, D, E }
|
||||
fnptr_format_args! { A, B, C, D, E, F }
|
||||
fnptr_format_args! { A, B, C, D, E, F, G }
|
||||
fnptr_format_args! { A, B, C, D, E, F, G, H }
|
||||
fnptr_format_args! { A, B, C, D, E, F, G, H, I }
|
||||
fnptr_format_args! { A, B, C, D, E, F, G, H, I, J }
|
||||
fnptr_format_args! { A, B, C, D, E, F, G, H, I, J, K }
|
||||
fnptr_format_args! { A, B, C, D, E, F, G, H, I, J, K, L }
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
use super::*;
|
||||
|
||||
impl<'a, T: 'a> Format for core::slice::ChunksExact<'a, T>
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "ChunksExact(..)")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Format for core::slice::Iter<'a, T>
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(
|
||||
fmt,
|
||||
"Iter {{ slice: {=[?]}, position: ? }}",
|
||||
self.as_slice()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Format for core::slice::Windows<'a, T>
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "Windows(..)")
|
||||
}
|
||||
}
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
macro_rules! default_format {
|
||||
() => {
|
||||
#[inline]
|
||||
fn format(&self, _fmt: Formatter) {
|
||||
crate::export::istr(&Self::_format_tag());
|
||||
self._format_data();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! delegate_format {
|
||||
($ty:ty, $self_:ident, $val:expr) => {
|
||||
#[inline]
|
||||
fn format(&$self_, fmt: Formatter) {
|
||||
<$ty as Format>::format($val, fmt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
<$ty as Format>::_format_tag()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&$self_) {
|
||||
<$ty as Format>::_format_data($val)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod adapter;
|
||||
#[cfg(feature = "alloc")]
|
||||
mod alloc_;
|
||||
mod arrays;
|
||||
mod core_;
|
||||
mod primitives;
|
||||
mod tuples;
|
||||
|
||||
use defmt_macros::internp;
|
||||
|
||||
use crate::{self as defmt, Format, Formatter, Str};
|
||||
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
use crate::export;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! prim {
|
||||
($ty:ty, $fmt: literal, $self_:ident, $write:expr) => {
|
||||
impl Format for $ty {
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!($fmt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&$self_) {
|
||||
$write
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
prim!(i8, "{=i8}", self, export::i8(self));
|
||||
prim!(i16, "{=i16}", self, export::i16(self));
|
||||
prim!(i32, "{=i32}", self, export::i32(self));
|
||||
prim!(i64, "{=i64}", self, export::i64(self));
|
||||
prim!(i128, "{=i128}", self, export::i128(self));
|
||||
prim!(isize, "{=isize}", self, export::isize(self));
|
||||
prim!(u8, "{=u8}", self, export::u8(self));
|
||||
prim!(u16, "{=u16}", self, export::u16(self));
|
||||
prim!(u32, "{=u32}", self, export::u32(self));
|
||||
prim!(u64, "{=u64}", self, export::u64(self));
|
||||
prim!(u128, "{=u128}", self, export::u128(self));
|
||||
prim!(usize, "{=usize}", self, export::usize(self));
|
||||
prim!(f32, "{=f32}", self, export::f32(self));
|
||||
prim!(f64, "{=f64}", self, export::f64(self));
|
||||
prim!(str, "{=str}", self, export::str(self));
|
||||
prim!(bool, "{=bool}", self, export::bool(self));
|
||||
prim!(Str, "{=istr}", self, export::istr(self));
|
||||
prim!(char, "{=char}", self, export::char(self));
|
||||
|
||||
impl<T> Format for [T]
|
||||
where
|
||||
T: Format,
|
||||
{
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("{=[?]}")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {
|
||||
export::fmt_slice(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Format for &'_ T
|
||||
where
|
||||
T: Format + ?Sized,
|
||||
{
|
||||
delegate_format!(T, self, self);
|
||||
}
|
||||
|
||||
impl<T> Format for &'_ mut T
|
||||
where
|
||||
T: Format + ?Sized,
|
||||
{
|
||||
delegate_format!(T, self, self);
|
||||
}
|
||||
|
||||
// Format raw pointer as hexadecimal
|
||||
//
|
||||
// First cast raw pointer to thin pointer, then to usize and finally format as hexadecimal.
|
||||
impl<T> Format for *const T
|
||||
where
|
||||
T: ?Sized,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
crate::write!(fmt, "0x{:x}", *self as *const () as usize);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Format for *mut T
|
||||
where
|
||||
T: ?Sized,
|
||||
{
|
||||
fn format(&self, fmt: Formatter) {
|
||||
Format::format(&(*self as *const T), fmt)
|
||||
}
|
||||
}
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
use super::*;
|
||||
use crate::export;
|
||||
|
||||
impl Format for () {
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("()")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _format_data(&self) {}
|
||||
}
|
||||
|
||||
macro_rules! tuple {
|
||||
( $format:expr, ($($name:ident),+) ) => (
|
||||
impl<$($name:Format),+> Format for ($($name,)+) where last_type!($($name,)+): ?Sized {
|
||||
default_format!();
|
||||
|
||||
#[inline]
|
||||
fn _format_tag() -> Str {
|
||||
internp!($format)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case, unused_assignments)]
|
||||
fn _format_data(&self) {
|
||||
let ($(ref $name,)+) = *self;
|
||||
$(
|
||||
export::istr(&$name::_format_tag());
|
||||
$name._format_data();
|
||||
)+
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! last_type {
|
||||
($a:ident,) => { $a };
|
||||
($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) };
|
||||
}
|
||||
|
||||
tuple! { "({=?})", (T0) }
|
||||
tuple! { "({=?}, {=?})", (T0, T1) }
|
||||
tuple! { "({=?}, {=?}, {=?})", (T0, T1, T2) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) }
|
||||
tuple! { "({=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?}, {=?})", (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) }
|
||||
Vendored
+467
@@ -0,0 +1,467 @@
|
||||
//! A highly efficient logging framework that targets resource-constrained
|
||||
//! devices, like microcontrollers.
|
||||
//!
|
||||
//! Check out the defmt book at <https://defmt.ferrous-systems.com> for more
|
||||
//! information about how to use it.
|
||||
//!
|
||||
//! # Compatibility
|
||||
//!
|
||||
//! The `defmt` wire format might change between minor versions. Attempting to
|
||||
//! read a defmt stream with an incompatible version will result in an error,
|
||||
//! and any tool used to process that stream should first check for a symbol
|
||||
//! named like `_defmt_version_ = X`, where X indicates the wire format version
|
||||
//! in use.
|
||||
//!
|
||||
//! Updating your version of defmt might mean you also have to update your
|
||||
//! version of `defmt-print` or `defmt-decoder`.
|
||||
|
||||
#![cfg_attr(not(feature = "unstable-test"), no_std)]
|
||||
// NOTE if you change this URL you'll also need to update all other crates in this repo
|
||||
#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
// This must be in the root lib.rs, otherwise it doesn't appear in the final binary.
|
||||
|
||||
/// The defmt ABI and wire format version.
|
||||
///
|
||||
/// This number has to be updated every time there is a backwards-incompatible change to
|
||||
/// - the symbol naming scheme
|
||||
/// - the symbol and section layout
|
||||
/// - the data encoding / wire format
|
||||
#[used]
|
||||
#[cfg_attr(target_os = "macos", link_section = ".defmt,end.VERSION")]
|
||||
#[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end")]
|
||||
#[export_name = "_defmt_version_ = 4"]
|
||||
static DEFMT_VERSION: u8 = 0;
|
||||
|
||||
#[used]
|
||||
#[cfg_attr(target_os = "macos", link_section = ".defmt,end.ENCODING")]
|
||||
#[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end")]
|
||||
#[cfg_attr(feature = "encoding-raw", export_name = "_defmt_encoding_ = raw")]
|
||||
#[cfg_attr(
|
||||
not(feature = "encoding-raw"),
|
||||
export_name = "_defmt_encoding_ = rzcobs"
|
||||
)]
|
||||
#[allow(missing_docs)]
|
||||
#[doc(hidden)]
|
||||
pub static DEFMT_ENCODING: u8 = 0;
|
||||
|
||||
mod encoding;
|
||||
#[doc(hidden)]
|
||||
pub mod export;
|
||||
mod formatter;
|
||||
mod impls;
|
||||
#[cfg(all(test, feature = "unstable-test"))]
|
||||
mod tests;
|
||||
mod traits;
|
||||
|
||||
pub use crate::{
|
||||
encoding::Encoder,
|
||||
formatter::{Formatter, Str},
|
||||
impls::adapter::{Debug2Format, Display2Format},
|
||||
traits::{Format, Logger},
|
||||
};
|
||||
|
||||
#[cfg(all(test, not(feature = "unstable-test")))]
|
||||
compile_error!(
|
||||
"to run unit tests enable the `unstable-test` feature, e.g. `cargo t --features unstable-test`"
|
||||
);
|
||||
|
||||
/// Just like the [`core::assert!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::assert!`]: https://doc.rust-lang.org/core/macro.assert.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::assert_ as assert;
|
||||
|
||||
/// Just like the [`core::assert_eq!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::assert_eq!`]: https://doc.rust-lang.org/core/macro.assert_eq.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::assert_eq_ as assert_eq;
|
||||
|
||||
/// Just like the [`core::assert_ne!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::assert_ne!`]: https://doc.rust-lang.org/core/macro.assert_ne.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::assert_ne_ as assert_ne;
|
||||
|
||||
/// Just like the [`core::debug_assert!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::debug_assert!`]: https://doc.rust-lang.org/core/macro.debug_assert.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::debug_assert_ as debug_assert;
|
||||
|
||||
/// Just like the [`core::debug_assert_eq!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::debug_assert_eq!`]: https://doc.rust-lang.org/core/macro.debug_assert_eq.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::debug_assert_eq_ as debug_assert_eq;
|
||||
|
||||
/// Just like the [`core::debug_assert_ne!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::debug_assert_ne!`]: https://doc.rust-lang.org/core/macro.debug_assert_ne.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::debug_assert_ne_ as debug_assert_ne;
|
||||
|
||||
/// Just like the [`core::unreachable!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::unreachable!`]: https://doc.rust-lang.org/core/macro.unreachable.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::unreachable_ as unreachable;
|
||||
|
||||
/// Just like the [`core::todo!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::todo!`]: https://doc.rust-lang.org/core/macro.todo.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::todo_ as todo;
|
||||
|
||||
/// Just like the [`core::unimplemented!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::unimplemented!`]: https://doc.rust-lang.org/core/macro.unimplemented.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::todo_ as unimplemented;
|
||||
|
||||
/// Just like the [`core::panic!`] macro but `defmt` is used to log the panic message
|
||||
///
|
||||
/// [`core::panic!`]: https://doc.rust-lang.org/core/macro.panic.html
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::panic_ as panic;
|
||||
|
||||
/// Unwraps an `Option` or `Result`, panicking if it is `None` or `Err`.
|
||||
///
|
||||
/// This macro is roughly equivalent to `{Option,Result}::{expect,unwrap}` but invocation looks
|
||||
/// a bit different because this is a macro and not a method. The other difference is that
|
||||
/// `unwrap!`-ing a `Result<T, E>` value requires that the error type `E` implements the `Format`
|
||||
/// trait
|
||||
///
|
||||
/// The following snippet shows the differences between core's unwrap method and defmt's unwrap
|
||||
/// macro:
|
||||
///
|
||||
/// ```
|
||||
/// use defmt::unwrap;
|
||||
///
|
||||
/// # let option = Some(());
|
||||
/// let x = option.unwrap();
|
||||
/// let x = unwrap!(option);
|
||||
///
|
||||
/// # let result = Ok::<(), ()>(());
|
||||
/// let x = result.unwrap();
|
||||
/// let x = unwrap!(result);
|
||||
///
|
||||
/// let x = result.expect("text");
|
||||
/// let x = unwrap!(result, "text");
|
||||
///
|
||||
/// # let arg = ();
|
||||
/// let x = result.expect(&format!("text {:?}", arg));
|
||||
/// let x = unwrap!(result, "text {:?}", arg); // arg must be implement `Format`
|
||||
/// ```
|
||||
///
|
||||
/// If used, the format string must follow the defmt syntax (documented in [the manual])
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::unwrap;
|
||||
|
||||
/// This is an alias for defmt's [`unwrap`] macro which supports messages like std's except.
|
||||
/// ```
|
||||
/// use defmt::expect;
|
||||
///
|
||||
/// # let result = Ok::<(), ()>(());
|
||||
/// # let arg = ();
|
||||
/// let x = result.expect(&format!("text {:?}", arg));
|
||||
/// let x = expect!(result, "text {:?}", arg); // arg must be implement `Format`
|
||||
/// ```
|
||||
///
|
||||
/// For the complete documentation see that of defmt's *unwrap* macro.
|
||||
// note: Linking to unwrap is broken as of 2024-10-09, it links back to expect
|
||||
pub use defmt_macros::unwrap as expect;
|
||||
|
||||
/// Overrides the panicking behavior of `defmt::panic!`
|
||||
///
|
||||
/// By default, `defmt::panic!` calls `core::panic!` after logging the panic message using `defmt`.
|
||||
/// This can result in the panic message being printed twice in some cases. To avoid that issue use
|
||||
/// this macro. See [the manual] for details.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/panic.html
|
||||
///
|
||||
/// # Inter-operation with built-in attributes
|
||||
///
|
||||
/// This attribute cannot be used together with the `export_name` or `no_mangle` attributes
|
||||
pub use defmt_macros::panic_handler;
|
||||
|
||||
/// Creates an interned string ([`Str`]) from a string literal.
|
||||
///
|
||||
/// This must be called on a string literal, and will allocate the literal in the object file. At
|
||||
/// runtime, only a small string index is required to refer to the string, represented as the
|
||||
/// [`Str`] type.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let interned = defmt::intern!("long string literal taking up little space");
|
||||
/// ```
|
||||
///
|
||||
/// [`Str`]: struct.Str.html
|
||||
pub use defmt_macros::intern;
|
||||
|
||||
/// Always logs data irrespective of log level.
|
||||
///
|
||||
/// Please refer to [the manual] for documentation on the syntax.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::println;
|
||||
|
||||
/// Logs data at *debug* level.
|
||||
///
|
||||
/// Please refer to [the manual] for documentation on the syntax.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::debug;
|
||||
/// Logs data at *error* level.
|
||||
///
|
||||
/// Please refer to [the manual] for documentation on the syntax.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::error;
|
||||
/// Logs data at *info* level.
|
||||
///
|
||||
/// Please refer to [the manual] for documentation on the syntax.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::info;
|
||||
/// Logs data at *trace* level.
|
||||
///
|
||||
/// Please refer to [the manual] for documentation on the syntax.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::trace;
|
||||
/// Logs data at *warn* level.
|
||||
///
|
||||
/// Please refer to [the manual] for documentation on the syntax.
|
||||
///
|
||||
/// [the manual]: https://defmt.ferrous-systems.com/macros.html
|
||||
pub use defmt_macros::warn;
|
||||
|
||||
/// Just like the [`std::dbg!`] macro but `defmt` is used to log the message at `TRACE` level.
|
||||
///
|
||||
/// [`std::dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
|
||||
pub use defmt_macros::dbg;
|
||||
|
||||
/// Writes formatted data to a [`Formatter`].
|
||||
///
|
||||
/// [`Formatter`]: struct.Formatter.html
|
||||
pub use defmt_macros::write;
|
||||
|
||||
/// Defines the global defmt logger.
|
||||
///
|
||||
/// `#[global_logger]` needs to be put on a unit struct type declaration. This struct has to
|
||||
/// implement the [`Logger`] trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use defmt::{Logger, global_logger};
|
||||
///
|
||||
/// #[global_logger]
|
||||
/// struct MyLogger;
|
||||
///
|
||||
/// unsafe impl Logger for MyLogger {
|
||||
/// fn acquire() {
|
||||
/// # todo!()
|
||||
/// // ...
|
||||
/// }
|
||||
/// unsafe fn flush() {
|
||||
/// # todo!()
|
||||
/// // ...
|
||||
/// }
|
||||
/// unsafe fn release() {
|
||||
/// # todo!()
|
||||
/// // ...
|
||||
/// }
|
||||
/// unsafe fn write(bytes: &[u8]) {
|
||||
/// # todo!()
|
||||
/// // ...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Logger`]: trait.Logger.html
|
||||
pub use defmt_macros::global_logger;
|
||||
|
||||
/// Defines the global timestamp provider for defmt.
|
||||
///
|
||||
/// This macro can be used to attach a timestamp or other data to every defmt message. Its syntax
|
||||
/// works exactly like the logging macros, except that no local variables can be accessed and the
|
||||
/// macro should be placed in a module instead of a function.
|
||||
///
|
||||
/// `timestamp!` must only be used once across the crate graph.
|
||||
///
|
||||
/// If no crate defines a timestamp, no timestamp will be included in the logged messages.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use core::sync::atomic::{AtomicU32, Ordering};
|
||||
///
|
||||
/// static COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
/// defmt::timestamp!("{=u32:us}", COUNT.fetch_add(1, Ordering::Relaxed));
|
||||
/// ```
|
||||
pub use defmt_macros::timestamp;
|
||||
|
||||
/// Generates a bitflags structure that can be formatted with defmt.
|
||||
///
|
||||
/// This macro is a wrapper around the [`bitflags!`] crate, and provides an (almost) identical
|
||||
/// interface. Refer to [its documentation] for an explanation of the syntax.
|
||||
///
|
||||
/// [its documentation]: https://docs.rs/bitflags/1/bitflags/
|
||||
///
|
||||
/// # Limitations
|
||||
///
|
||||
/// This macro only supports bitflags structs represented as one of Rust's built-in unsigned integer
|
||||
/// types (`u8`, `u16`, `u32`, `u64`, or `u128`). Custom types are not supported. This restriction
|
||||
/// is necessary to support defmt's efficient encoding.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The example from the bitflags crate works as-is:
|
||||
///
|
||||
/// ```
|
||||
/// defmt::bitflags! {
|
||||
/// struct Flags: u32 {
|
||||
/// const A = 0b00000001;
|
||||
/// const B = 0b00000010;
|
||||
/// const C = 0b00000100;
|
||||
/// const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// defmt::info!("Flags::ABC: {}", Flags::ABC);
|
||||
/// defmt::info!("Flags::empty(): {}", Flags::empty());
|
||||
/// ```
|
||||
pub use defmt_macros::bitflags;
|
||||
|
||||
#[doc(hidden)] // documented as the `Format` trait instead
|
||||
pub use defmt_macros::Format;
|
||||
|
||||
// There is no default timestamp format. Instead, the decoder looks for a matching ELF symbol. If
|
||||
// absent, timestamps are turned off.
|
||||
#[export_name = "__defmt_default_timestamp"]
|
||||
fn default_timestamp(_f: Formatter<'_>) {}
|
||||
|
||||
#[export_name = "__defmt_default_panic"]
|
||||
fn default_panic() -> ! {
|
||||
core::panic!()
|
||||
}
|
||||
|
||||
/// Block until host has read all pending data.
|
||||
///
|
||||
/// The flush operation will not fail, but might not succeed in flushing _all_ pending data. It is
|
||||
/// implemented as a "best effort" operation.
|
||||
///
|
||||
/// This calls the method `flush` of the used "global [`Logger`]". The logger is likely provided by
|
||||
/// [`defmt-rtt`](https://crates.io/crates/defmt-rtt) or [`defmt-itm`](https://crates.io/crates/defmt-itm).
|
||||
pub fn flush() {
|
||||
match () {
|
||||
#[cfg(feature = "unstable-test")]
|
||||
() => {
|
||||
// no-op when run on host
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
() => {
|
||||
extern "Rust" {
|
||||
fn _defmt_acquire();
|
||||
fn _defmt_flush();
|
||||
fn _defmt_release();
|
||||
}
|
||||
// SAFETY:
|
||||
// * we call these function in the correct order: first acquire the lock, then flush and
|
||||
// finally release the lock
|
||||
// * these function should be provided by the macro `#[global_logger]` and therefore
|
||||
// trustworthy to call through FFI-bounds
|
||||
unsafe {
|
||||
_defmt_acquire();
|
||||
_defmt_flush();
|
||||
_defmt_release()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
#[doc(hidden)]
|
||||
pub struct IdRanges {
|
||||
pub trace: core::ops::Range<u16>,
|
||||
pub debug: core::ops::Range<u16>,
|
||||
pub info: core::ops::Range<u16>,
|
||||
pub warn: core::ops::Range<u16>,
|
||||
pub error: core::ops::Range<u16>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-test"))]
|
||||
impl IdRanges {
|
||||
pub fn get() -> Self {
|
||||
extern "C" {
|
||||
static __DEFMT_MARKER_TRACE_START: u8;
|
||||
static __DEFMT_MARKER_TRACE_END: u8;
|
||||
static __DEFMT_MARKER_DEBUG_START: u8;
|
||||
static __DEFMT_MARKER_DEBUG_END: u8;
|
||||
static __DEFMT_MARKER_INFO_START: u8;
|
||||
static __DEFMT_MARKER_INFO_END: u8;
|
||||
static __DEFMT_MARKER_WARN_START: u8;
|
||||
static __DEFMT_MARKER_WARN_END: u8;
|
||||
static __DEFMT_MARKER_ERROR_START: u8;
|
||||
static __DEFMT_MARKER_ERROR_END: u8;
|
||||
}
|
||||
|
||||
let trace_start = unsafe { &__DEFMT_MARKER_TRACE_START as *const u8 as u16 };
|
||||
let trace_end = unsafe { &__DEFMT_MARKER_TRACE_END as *const u8 as u16 };
|
||||
let debug_start = unsafe { &__DEFMT_MARKER_DEBUG_START as *const u8 as u16 };
|
||||
let debug_end = unsafe { &__DEFMT_MARKER_DEBUG_END as *const u8 as u16 };
|
||||
let info_start = unsafe { &__DEFMT_MARKER_INFO_START as *const u8 as u16 };
|
||||
let info_end = unsafe { &__DEFMT_MARKER_INFO_END as *const u8 as u16 };
|
||||
let warn_start = unsafe { &__DEFMT_MARKER_WARN_START as *const u8 as u16 };
|
||||
let warn_end = unsafe { &__DEFMT_MARKER_WARN_END as *const u8 as u16 };
|
||||
let error_start = unsafe { &__DEFMT_MARKER_ERROR_START as *const u8 as u16 };
|
||||
let error_end = unsafe { &__DEFMT_MARKER_ERROR_END as *const u8 as u16 };
|
||||
Self {
|
||||
trace: trace_start..trace_end,
|
||||
debug: debug_start..debug_end,
|
||||
info: info_start..info_end,
|
||||
warn: warn_start..warn_end,
|
||||
error: error_start..error_end,
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
use crate as defmt;
|
||||
|
||||
#[test]
|
||||
fn log_levels() {
|
||||
// just make sure they build OK for now
|
||||
defmt::trace!("test trace");
|
||||
defmt::debug!("test debug");
|
||||
defmt::info!("test info");
|
||||
defmt::warn!("test warn");
|
||||
defmt::error!("test error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str() {
|
||||
defmt::info!("Hello, {=str}", "world");
|
||||
|
||||
let world = defmt::intern!("world");
|
||||
defmt::info!("Hello, {=istr}", world);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_comma() {
|
||||
defmt::trace!("test trace",);
|
||||
defmt::debug!("test debug",);
|
||||
defmt::info!("test info",);
|
||||
defmt::warn!("test warn",);
|
||||
defmt::error!("test error",);
|
||||
|
||||
defmt::trace!("test trace {=?}", 0,);
|
||||
defmt::debug!("test debug {=?}", 0,);
|
||||
defmt::info!("test info {=?}", 0,);
|
||||
defmt::warn!("test warn {=?}", 0,);
|
||||
defmt::error!("test error {=?}", 0,);
|
||||
}
|
||||
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
use defmt_macros::internp;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate as defmt;
|
||||
use crate::{export, Formatter, Str};
|
||||
|
||||
/// Trait for types that can be formatted via defmt.
|
||||
///
|
||||
/// This trait is used by the `{:?}` format specifier and can format a wide range of types.
|
||||
/// User-defined types can `#[derive(Format)]` to get an auto-generated implementation of this
|
||||
/// trait.
|
||||
///
|
||||
/// **Note**: The implementation of `#[derive(Format)]` assumes that no builtin types are shadowed
|
||||
/// (for example by defining a `struct u8;`). This allows it to represent them more compactly.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Usually, an implementation of this trait can be `#[derive]`d automatically:
|
||||
///
|
||||
/// ```
|
||||
/// use defmt::Format;
|
||||
///
|
||||
/// #[derive(Format)]
|
||||
/// struct Header {
|
||||
/// source: u8,
|
||||
/// destination: u8,
|
||||
/// sequence: u16,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Manual implementations can make use of the [`write!`] macro:
|
||||
///
|
||||
/// ```
|
||||
/// use defmt::{Format, Formatter, write};
|
||||
///
|
||||
/// struct Id(u32);
|
||||
///
|
||||
/// impl Format for Id {
|
||||
/// fn format(&self, fmt: Formatter) {
|
||||
/// // Format as hexadecimal.
|
||||
/// write!(fmt, "Id({:x})", self.0);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// **Note** Some implementations of standard types like `Vec<T>` are hidden behind the `alloc` feature flag.
|
||||
pub trait Format {
|
||||
/// Writes the defmt representation of `self` to `fmt`.
|
||||
fn format(&self, fmt: Formatter);
|
||||
|
||||
#[doc(hidden)]
|
||||
fn _format_tag() -> Str {
|
||||
internp!("{=__internal_FormatSequence}")
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn _format_data(&self) {
|
||||
self.format(export::make_formatter());
|
||||
export::u16(&0); // terminator
|
||||
}
|
||||
}
|
||||
|
||||
/// Global logger acquire-release mechanism
|
||||
///
|
||||
/// This trait's methods will be called by the defmt logging macros to transmit the
|
||||
/// encoded log data over the wire. The call order is:
|
||||
/// - One `acquire()` call to start the log frame.
|
||||
/// - Multiple `write()` calls, with fragments of the log frame data each.
|
||||
/// - One `release()` call.
|
||||
///
|
||||
/// The data passed to `write()` is *unencoded*. Implementations MUST encode it with `Encoder`
|
||||
/// prior to sending it over the wire. The simplest way is for `acquire()` to call `Encoder::start_frame()`,
|
||||
/// `write()` to call `Encoder::write()`, and `release()` to call `Encoder::end_frame()`.
|
||||
///
|
||||
/// The global logger can be acquired once for each "execution context". The definition
|
||||
/// of execution context is up to the implementation. For example, it can be:
|
||||
///
|
||||
/// - the entire process.
|
||||
/// - one thread in std environments.
|
||||
/// - one interrupt priority level in embedded devices.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `acquire` logically acquires the global logger in the current execution context.
|
||||
/// The acquiring is tracked internally, no Rust object is returned representing ownership.
|
||||
/// - `acquire` is a safe function, therefore it must be thread-safe and interrupt-safe
|
||||
///
|
||||
/// And, not safety related, the methods should never be invoked from user code. The easiest way to
|
||||
/// ensure this is to implement `Logger` on a *private* `struct` and mark that `struct` as the
|
||||
/// `#[global_logger]`.
|
||||
pub unsafe trait Logger {
|
||||
/// Acquire the global logger in the current execution context.
|
||||
///
|
||||
/// This will be called by the defmt logging macros before writing each log frame.
|
||||
///
|
||||
/// Panics if already acquired in the current execution context. Otherwise it must never fail.
|
||||
fn acquire();
|
||||
|
||||
/// Block until host has read all pending data.
|
||||
///
|
||||
/// The flush operation must not fail. This is a "best effort" operation, I/O errors should be discarded.
|
||||
///
|
||||
/// # Safety
|
||||
/// Must only be called when the global logger is acquired in the current execution context.
|
||||
/// (i.e. between `acquire()` and `release()`).
|
||||
unsafe fn flush();
|
||||
|
||||
/// Releases the global logger in the current execution context.
|
||||
///
|
||||
/// This will be called by the defmt logging macros after writing each log frame.
|
||||
///
|
||||
/// # Safety
|
||||
/// Must be called exactly once for each acquire(), in the same execution context.
|
||||
unsafe fn release();
|
||||
|
||||
/// Writes `bytes` to the destination.
|
||||
///
|
||||
/// This will be called by the defmt logging macros to transmit frame data. One log frame may cause multiple `write` calls.
|
||||
///
|
||||
/// The write operation must not fail. This is a "best effort" operation, I/O errors should be discarded.
|
||||
///
|
||||
/// The `bytes` are unencoded log frame data, they MUST be encoded with `Encoder` prior to
|
||||
/// sending over the wire.
|
||||
///
|
||||
/// Note that a call to `write` does *not* correspond to a defmt logging macro invocation. A
|
||||
/// single `defmt::info!` call can result in an arbitrary number of `write` calls.
|
||||
///
|
||||
/// # Safety
|
||||
/// Must only be called when the global logger is acquired in the current execution context.
|
||||
/// (i.e. between `acquire()` and `release()`).
|
||||
unsafe fn write(bytes: &[u8]);
|
||||
}
|
||||
Reference in new issue
Block a user