Skip to main content

defmt_decoder/
lib.rs

1//! Decodes [`defmt`](https://github.com/knurling-rs/defmt) log frames
2//!
3//! NOTE: The decoder always runs on the host!
4
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![doc(html_logo_url = "https://knurling.ferrous-systems.com/knurling_logo_light_text.svg")]
7
8pub const DEFMT_VERSIONS: &[&str] = &["3", "4"];
9// To avoid a breaking change, still provide `DEFMT_VERSION`.
10#[deprecated = "Please use DEFMT_VERSIONS instead"]
11pub const DEFMT_VERSION: &str = DEFMT_VERSIONS[1];
12
13mod decoder;
14mod elf2table;
15mod frame;
16pub mod log;
17mod stream;
18
19use std::{
20    collections::{BTreeMap, HashMap},
21    error::Error,
22    fmt, io,
23    str::FromStr,
24};
25
26use byteorder::{ReadBytesExt, LE};
27use defmt_parser::Level;
28use serde::{Deserialize, Serialize};
29
30use crate::{decoder::Decoder, elf2table::parse_impl};
31
32pub use crate::{
33    elf2table::{Location, Locations},
34    frame::Frame,
35    stream::StreamDecoder,
36};
37
38/// Specifies the origin of a format string
39#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
40pub enum Tag {
41    /// Defmt-controlled format string for primitive types.
42    Prim,
43    /// Format string created by `#[derive(Format)]`.
44    Derived,
45    /// Format string created by `defmt::bitflags!`.
46    Bitflags,
47    /// A user-defined format string from a `write!` invocation.
48    Write,
49    /// An interned string, for use with `{=istr}`.
50    Str,
51    /// Defines the global timestamp format.
52    Timestamp,
53
54    /// `static` containing a possible value of a bitflags type.
55    BitflagsValue,
56    /// Format string created by `defmt::println!`.
57    Println,
58
59    Trace,
60    Debug,
61    Info,
62    Warn,
63    Error,
64}
65
66impl Tag {
67    fn to_level(&self) -> Option<Level> {
68        match self {
69            Tag::Trace => Some(Level::Trace),
70            Tag::Debug => Some(Level::Debug),
71            Tag::Info => Some(Level::Info),
72            Tag::Warn => Some(Level::Warn),
73            Tag::Error => Some(Level::Error),
74            _ => None,
75        }
76    }
77}
78
79/// Entry in [`Table`] combining a format string with its raw symbol
80#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
81pub struct TableEntry {
82    string: StringEntry,
83    raw_symbol: String,
84}
85
86impl TableEntry {
87    pub fn new(string: StringEntry, raw_symbol: String) -> Self {
88        Self { string, raw_symbol }
89    }
90
91    #[cfg(test)]
92    fn new_without_symbol(tag: Tag, string: String) -> Self {
93        Self {
94            string: StringEntry::new(tag, string),
95            raw_symbol: "<unknown>".to_string(),
96        }
97    }
98}
99
100/// A format string and it's [`Tag`]
101#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
102pub struct StringEntry {
103    tag: Tag,
104    string: String,
105}
106
107impl StringEntry {
108    pub fn new(tag: Tag, string: String) -> Self {
109        Self { tag, string }
110    }
111}
112
113/// Data that uniquely identifies a `defmt::bitflags!` invocation.
114#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
115struct BitflagsKey {
116    /// Name of the bitflags struct (this is really redundant with `disambig`).
117    ident: String,
118    package: String,
119    disambig: String,
120    crate_name: Option<String>,
121}
122
123/// How a defmt frame is encoded
124#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
125#[non_exhaustive]
126pub enum Encoding {
127    /// raw data, that is no encoding.
128    Raw,
129    /// [Reverse Zero-compressing COBS encoding](https://github.com/Dirbaio/rzcobs)
130    Rzcobs,
131}
132
133impl FromStr for Encoding {
134    type Err = anyhow::Error;
135
136    fn from_str(s: &str) -> Result<Self, Self::Err> {
137        match s {
138            "raw" => Ok(Encoding::Raw),
139            "rzcobs" => Ok(Encoding::Rzcobs),
140            _ => anyhow::bail!("Unknown defmt encoding '{}' specified. This is a bug.", s),
141        }
142    }
143}
144
145impl Encoding {
146    /// Can this encoding recover from missed bytes?
147    pub const fn can_recover(&self) -> bool {
148        match self {
149            Encoding::Raw => false,
150            Encoding::Rzcobs => true,
151        }
152    }
153}
154
155/// Internal table that holds log levels and maps format strings to indices
156#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
157pub struct Table {
158    timestamp: Option<TableEntry>,
159    entries: BTreeMap<usize, TableEntry>,
160    bitflags: HashMap<BitflagsKey, Vec<(String, u128)>>,
161    encoding: Encoding,
162}
163
164impl Table {
165    /// Parses an ELF file and returns the decoded `defmt` table.
166    ///
167    /// This function returns `None` if the ELF file contains no `.defmt` section.
168    pub fn parse(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
169        parse_impl(elf, true)
170    }
171
172    /// Like `parse`, but does not verify that the defmt version in the firmware matches the host.
173    ///
174    /// CAUTION: This is meant for defmt/probe-run development only and can result in reading garbage data.
175    pub fn parse_ignore_version(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
176        parse_impl(elf, false)
177    }
178
179    pub fn set_timestamp_entry(&mut self, timestamp: TableEntry) {
180        self.timestamp = Some(timestamp);
181    }
182
183    fn _get(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
184        let entry = self.entries.get(&index).ok_or(())?;
185        Ok((entry.string.tag.to_level(), &entry.string.string))
186    }
187
188    fn get_with_level(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
189        self._get(index)
190    }
191
192    fn get_without_level(&self, index: usize) -> Result<&str, ()> {
193        let (lvl, format) = self._get(index)?;
194        if lvl.is_none() {
195            Ok(format)
196        } else {
197            Err(())
198        }
199    }
200
201    pub fn indices(&self) -> impl Iterator<Item = usize> + '_ {
202        self.entries.iter().filter_map(move |(idx, entry)| {
203            if entry.string.tag.to_level().is_some() || entry.string.tag == Tag::Println {
204                Some(*idx)
205            } else {
206                None
207            }
208        })
209    }
210
211    pub fn is_empty(&self) -> bool {
212        self.entries.is_empty()
213    }
214
215    /// Iterates over the raw symbols of the table entries
216    pub fn raw_symbols(&self) -> impl Iterator<Item = &str> + '_ {
217        self.entries.values().map(|s| &*s.raw_symbol)
218    }
219
220    pub fn get_locations(&self, elf: &[u8]) -> Result<Locations, anyhow::Error> {
221        elf2table::get_locations(elf, self)
222    }
223
224    /// Decode the data sent by the device using the previously stored metadata.
225    ///
226    /// * `bytes`
227    ///   * contains the data sent by the device that logs.
228    ///   * contains the [log string index, timestamp, optional fmt string args]
229    pub fn decode<'t>(
230        &'t self,
231        mut bytes: &[u8],
232    ) -> Result<(Frame<'t>, /* consumed: */ usize), DecodeError> {
233        let len = bytes.len();
234        let index = bytes.read_u16::<LE>()? as u64;
235
236        let mut decoder = Decoder::new(self, bytes);
237
238        let mut timestamp_format = None;
239        let mut timestamp_args = Vec::new();
240        if let Some(entry) = self.timestamp.as_ref() {
241            let format = &entry.string.string;
242            timestamp_format = Some(&**format);
243            timestamp_args = decoder.decode_format(format)?;
244        }
245
246        let (level, format) = self
247            .get_with_level(index as usize)
248            .map_err(|_| DecodeError::Malformed)?;
249
250        let args = decoder.decode_format(format)?;
251
252        let frame = Frame::new(
253            self,
254            level,
255            index,
256            timestamp_format,
257            timestamp_args,
258            format,
259            args,
260        );
261
262        let consumed = len - decoder.bytes.len();
263        Ok((frame, consumed))
264    }
265
266    pub fn new_stream_decoder(&self) -> Box<dyn StreamDecoder + Send + Sync + '_> {
267        match self.encoding {
268            Encoding::Raw => Box::new(stream::Raw::new(self)),
269            Encoding::Rzcobs => Box::new(stream::Rzcobs::new(self)),
270        }
271    }
272
273    pub fn encoding(&self) -> Encoding {
274        self.encoding
275    }
276
277    pub fn has_timestamp(&self) -> bool {
278        self.timestamp.is_some()
279    }
280}
281
282// NOTE follows `parser::Type`
283#[derive(Debug, Clone, PartialEq)]
284enum Arg<'t> {
285    /// Bool
286    Bool(bool),
287    F32(f32),
288    F64(f64),
289    /// U8, U16, U32, U64, U128
290    Uxx(u128),
291    /// I8, I16, I32, I64, I128
292    Ixx(i128),
293    /// Str
294    Str(String),
295    /// Interned string
296    IStr(&'t str),
297    /// Format
298    Format {
299        format: &'t str,
300        args: Vec<Arg<'t>>,
301    },
302    FormatSlice {
303        elements: Vec<FormatSliceElement<'t>>,
304    },
305    FormatSequence {
306        args: Vec<Arg<'t>>,
307    },
308    /// Slice or Array of bytes.
309    Slice(Vec<u8>),
310    /// Char
311    Char(char),
312
313    /// `fmt::Debug` / `fmt::Display` formatted on-target.
314    Preformatted(String),
315}
316
317#[derive(Debug, Clone, PartialEq)]
318struct FormatSliceElement<'t> {
319    // this will usually be the same format string for all elements; except when the format string
320    // is an enum -- in that case `format` will be the variant
321    format: &'t str,
322    args: Vec<Arg<'t>>,
323}
324
325/// Ways in which decoding a defmt frame can fail.
326#[derive(Debug, Eq, PartialEq)]
327pub enum DecodeError {
328    /// More data is needed to decode the next frame.
329    UnexpectedEof,
330    /// The frame was not in the expected format.
331    Malformed,
332}
333
334impl From<io::Error> for DecodeError {
335    fn from(e: io::Error) -> Self {
336        if e.kind() == io::ErrorKind::UnexpectedEof {
337            DecodeError::UnexpectedEof
338        } else {
339            DecodeError::Malformed
340        }
341    }
342}
343
344impl fmt::Display for DecodeError {
345    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346        match self {
347            DecodeError::UnexpectedEof => f.write_str("unexpected end of stream"),
348            DecodeError::Malformed => f.write_str("malformed data"),
349        }
350    }
351}
352
353impl Error for DecodeError {}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358
359    fn test_table(entries: impl IntoIterator<Item = TableEntry>) -> Table {
360        Table {
361            timestamp: None,
362            entries: entries.into_iter().enumerate().collect(),
363            bitflags: Default::default(),
364            encoding: Encoding::Raw,
365        }
366    }
367
368    fn test_table_with_timestamp(
369        entries: impl IntoIterator<Item = TableEntry>,
370        timestamp: &str,
371    ) -> Table {
372        Table {
373            timestamp: Some(TableEntry::new_without_symbol(
374                Tag::Timestamp,
375                timestamp.into(),
376            )),
377            entries: entries.into_iter().enumerate().collect(),
378            bitflags: Default::default(),
379            encoding: Encoding::Raw,
380        }
381    }
382
383    // helper function to initiate decoding and assert that the result is as expected.
384    //
385    // format:       format string to be expanded
386    // bytes:        arguments + metadata
387    // expectation:  the expected result
388    fn decode_and_expect(format: &str, bytes: &[u8], expectation: &str) {
389        let mut entries = BTreeMap::new();
390        entries.insert(
391            bytes[0] as usize,
392            TableEntry::new_without_symbol(Tag::Info, format.to_string()),
393        );
394
395        let table = Table {
396            entries,
397            timestamp: Some(TableEntry::new_without_symbol(
398                Tag::Timestamp,
399                "{=u8:us}".to_owned(),
400            )),
401            bitflags: Default::default(),
402            encoding: Encoding::Raw,
403        };
404
405        let frame = table.decode(bytes).unwrap().0;
406        assert_eq!(frame.display(false).to_string(), expectation.to_owned());
407    }
408
409    #[test]
410    fn decode() {
411        let entries = vec![
412            TableEntry::new_without_symbol(Tag::Info, "Hello, world!".to_owned()),
413            TableEntry::new_without_symbol(Tag::Debug, "The answer is {=u8}!".to_owned()),
414        ];
415
416        let table = test_table(entries);
417
418        let bytes = [0, 0];
419        //     index ^
420
421        assert_eq!(
422            table.decode(&bytes),
423            Ok((
424                Frame::new(
425                    &table,
426                    Some(Level::Info),
427                    0,
428                    None,
429                    vec![],
430                    "Hello, world!",
431                    vec![],
432                ),
433                bytes.len(),
434            ))
435        );
436
437        let bytes = [
438            1, 0,  // index
439            42, // argument
440        ];
441
442        assert_eq!(
443            table.decode(&bytes),
444            Ok((
445                Frame::new(
446                    &table,
447                    Some(Level::Debug),
448                    1,
449                    None,
450                    vec![],
451                    "The answer is {=u8}!",
452                    vec![Arg::Uxx(42)],
453                ),
454                bytes.len(),
455            ))
456        );
457
458        // TODO Format ({:?})
459    }
460
461    #[test]
462    fn all_integers() {
463        const FMT: &str =
464            "Hello, {=u8} {=u16} {=u32} {=u64} {=u128} {=i8} {=i16} {=i32} {=i64} {=i128}!";
465
466        let entries = vec![TableEntry::new_without_symbol(Tag::Info, FMT.to_owned())];
467
468        let table = test_table(entries);
469
470        let bytes = [
471            0, 0,  // index
472            42, // u8
473            0xff, 0xff, // u16
474            0xff, 0xff, 0xff, 0xff, // u32
475            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // u64
476            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
477            0xff, 0xff, // u128
478            0xff, // i8
479            0xff, 0xff, // i16
480            0xff, 0xff, 0xff, 0xff, // i32
481            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // i64
482            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
483            0xff, 0xff, // i128
484        ];
485
486        assert_eq!(
487            table.decode(&bytes),
488            Ok((
489                Frame::new(
490                    &table,
491                    Some(Level::Info),
492                    0,
493                    None,
494                    vec![],
495                    FMT,
496                    vec![
497                        Arg::Uxx(42),              // u8
498                        Arg::Uxx(u16::MAX.into()), // u16
499                        Arg::Uxx(u32::MAX.into()), // u32
500                        Arg::Uxx(u64::MAX.into()), // u64
501                        Arg::Uxx(u128::MAX),       // u128
502                        Arg::Ixx(-1),              // i8
503                        Arg::Ixx(-1),              // i16
504                        Arg::Ixx(-1),              // i32
505                        Arg::Ixx(-1),              // i64
506                        Arg::Ixx(-1),              // i128
507                    ],
508                ),
509                bytes.len(),
510            ))
511        );
512    }
513
514    #[test]
515    fn indices() {
516        let entries = vec![
517            TableEntry::new_without_symbol(Tag::Info, "The answer is {0=u8} {0=u8}!".to_owned()),
518            TableEntry::new_without_symbol(
519                Tag::Info,
520                "The answer is {1=u16} {0=u8} {1=u16}!".to_owned(),
521            ),
522        ];
523
524        let table = test_table(entries);
525        let bytes = [
526            0, 0,  // index
527            42, // argument
528        ];
529
530        assert_eq!(
531            table.decode(&bytes),
532            Ok((
533                Frame::new(
534                    &table,
535                    Some(Level::Info),
536                    0,
537                    None,
538                    vec![],
539                    "The answer is {0=u8} {0=u8}!",
540                    vec![Arg::Uxx(42)],
541                ),
542                bytes.len(),
543            ))
544        );
545
546        let bytes = [
547            1, 0,  // index
548            42, // u8
549            0xff, 0xff, // u16
550        ];
551
552        assert_eq!(
553            table.decode(&bytes),
554            Ok((
555                Frame::new(
556                    &table,
557                    Some(Level::Info),
558                    1,
559                    None,
560                    vec![],
561                    "The answer is {1=u16} {0=u8} {1=u16}!",
562                    vec![Arg::Uxx(42), Arg::Uxx(0xffff)],
563                ),
564                bytes.len(),
565            ))
566        );
567    }
568
569    #[test]
570    fn format() {
571        let entries = vec![
572            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
573            TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
574        ];
575
576        let table = test_table(entries);
577
578        let bytes = [
579            0, 0, // index
580            1, 0,  // index of the struct
581            42, // Foo.x
582        ];
583
584        assert_eq!(
585            table.decode(&bytes),
586            Ok((
587                Frame::new(
588                    &table,
589                    Some(Level::Info),
590                    0,
591                    None,
592                    vec![],
593                    "x={=?}",
594                    vec![Arg::Format {
595                        format: "Foo {{ x: {=u8} }}",
596                        args: vec![Arg::Uxx(42)]
597                    }],
598                ),
599                bytes.len(),
600            ))
601        );
602    }
603
604    #[test]
605    fn format_sequence() {
606        let entries = vec![
607            TableEntry::new_without_symbol(Tag::Info, "{=__internal_FormatSequence}".to_owned()),
608            TableEntry::new_without_symbol(Tag::Derived, "Foo".to_owned()),
609            TableEntry::new_without_symbol(Tag::Derived, "Bar({=u8})".to_owned()),
610            TableEntry::new_without_symbol(Tag::Derived, "State {=u8}|".to_owned()),
611        ];
612
613        let table = test_table(entries);
614
615        let bytes = [
616            0, 0, // index
617            1, 0, // index of Foo
618            2, 0,  // index of Bar
619            42, // bar.x
620            3, 0,  // index of State
621            23, // State variable
622            0, 0, // terminator
623        ];
624
625        assert_eq!(
626            table.decode(&bytes),
627            Ok((
628                Frame::new(
629                    &table,
630                    Some(Level::Info),
631                    0,
632                    None,
633                    vec![],
634                    "{=__internal_FormatSequence}",
635                    vec![Arg::FormatSequence {
636                        args: vec![
637                            Arg::Format {
638                                format: "Foo",
639                                args: vec![]
640                            },
641                            Arg::Format {
642                                format: "Bar({=u8})",
643                                args: vec![Arg::Uxx(42)]
644                            },
645                            Arg::Format {
646                                format: "State {=u8}|",
647                                args: vec![Arg::Uxx(23)]
648                            }
649                        ]
650                    }],
651                ),
652                bytes.len(),
653            ))
654        );
655    }
656
657    #[test]
658    fn display() {
659        let entries = vec![
660            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
661            TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
662        ];
663
664        let table = test_table_with_timestamp(entries, "{=u8:us}");
665
666        let bytes = [
667            0, 0, // index
668            2, // timestamp
669            1, 0,  // index of the struct
670            42, // Foo.x
671        ];
672
673        let frame = table.decode(&bytes).unwrap().0;
674        assert_eq!(
675            frame.display(false).to_string(),
676            "0.000002 INFO x=Foo { x: 42 }"
677        );
678    }
679
680    #[test]
681    fn display_message() {
682        let entries = vec![
683            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
684            TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
685        ];
686
687        let table = test_table(entries);
688
689        let bytes = [
690            0, 0, // index
691            1, 0,  // index of the struct
692            42, // Foo.x
693        ];
694
695        let frame = table.decode(&bytes).unwrap().0;
696        assert_eq!(frame.display_message().to_string(), "x=Foo { x: 42 }");
697    }
698
699    #[test]
700    fn display_fragments() {
701        let entries = vec![
702            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
703            TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
704        ];
705
706        let table = test_table(entries);
707
708        let bytes = [
709            0, 0, // index
710            1, 0,  // index of the struct
711            42, // Foo.x
712        ];
713
714        let frame = table.decode(&bytes).unwrap().0;
715        assert_eq!(
716            frame.display_fragments().collect::<Vec<String>>(),
717            ["x=", "Foo { x: 42 }"],
718        );
719    }
720
721    #[test]
722    fn display_i16_with_hex_hint() {
723        // defmt::info!("x: {=i16:#x},y: {=i16:#x},z: {=i16:#x}", -1_i16, -100_i16, -1000_i16);
724        let bytes = [
725            0,
726            0,           // index
727            2,           // timestamp
728            0b1111_1111, // the logged i16 value -1
729            0b1111_1111,
730        ];
731
732        decode_and_expect(
733            "i16 as hex {=i16:#x}",
734            &bytes,
735            "0.000002 INFO i16 as hex 0xffff",
736        );
737    }
738
739    #[test]
740    fn display_use_inner_type_hint() {
741        let entries = vec![
742            TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
743            TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:x} }}".to_owned()),
744        ];
745
746        let table = test_table_with_timestamp(entries, "{=u8:us}");
747
748        let bytes = [
749            0, 0, // index
750            2, // timestamp
751            1, 0,  // index of the struct
752            42, // value
753        ];
754
755        let frame = table.decode(&bytes).unwrap().0;
756        assert_eq!(
757            frame.display(false).to_string(),
758            "0.000002 INFO x=S { x: 2a }",
759        );
760        assert_eq!(
761            frame.display_fragments().collect::<Vec<String>>(),
762            ["x=", "S { x: 2a }"],
763        );
764    }
765
766    #[test]
767    fn display_use_outer_type_hint() {
768        let entries = vec![
769            TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
770            TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:?} }}".to_owned()),
771        ];
772
773        let table = test_table_with_timestamp(entries, "{=u8:us}");
774
775        let bytes = [
776            0, 0, // index
777            2, // timestamp
778            1, 0,  // index of the struct
779            42, // value
780        ];
781
782        let frame = table.decode(&bytes).unwrap().0;
783        assert_eq!(
784            frame.display(false).to_string(),
785            "0.000002 INFO x=S { x: 101010 }",
786        );
787        assert_eq!(
788            frame.display_fragments().collect::<Vec<String>>(),
789            ["x=", "S { x: 101010 }"],
790        );
791    }
792
793    #[test]
794    fn display_inner_str_in_struct() {
795        let entries = vec![
796            TableEntry::new_without_symbol(Tag::Info, "{}".to_owned()),
797            TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=str:?} }}".to_owned()),
798        ];
799
800        let table = test_table_with_timestamp(entries, "{=u8:us}");
801
802        let bytes = [
803            0, 0, // index
804            2, // timestamp
805            1, 0, // index into the struct
806            5, 0, 0, 0, // length of the string
807            b'H', b'e', b'l', b'l', b'o', // string "Hello"
808        ];
809        let frame = table.decode(&bytes).unwrap().0;
810        assert_eq!(
811            frame.display(false).to_string(),
812            "0.000002 INFO S { x: \"Hello\" }",
813        );
814        assert_eq!(
815            frame.display_fragments().collect::<Vec<String>>(),
816            ["S { x: \"Hello\" }"],
817        );
818    }
819
820    #[test]
821    fn display_u8_vec() {
822        let entries = vec![
823            TableEntry::new_without_symbol(Tag::Prim, "{=u8}".to_owned()),
824            TableEntry::new_without_symbol(Tag::Prim, "{=[?]}".to_owned()),
825            TableEntry::new_without_symbol(Tag::Derived, "Data {{ name: {=?:?} }}".to_owned()),
826            TableEntry::new_without_symbol(Tag::Info, "{=[?]:a}".to_owned()),
827        ];
828
829        let table = test_table_with_timestamp(entries, "{=u8:us}");
830
831        let bytes = [
832            3, 0, // frame index
833            2, // timestamp value of type `u8`
834            1, 0, 0, 0, // number of elements in `FormatSlice`
835            2, 0, // index to `Data` struct
836            1, 0, // Format index to table entry: `{=[?]}`
837            2, 0, 0, 0, // inner FormatSlice, number of elements in `name` field
838            0, 0,   // Format index to table entry: `{=u8}`
839            72,  // "H"
840            105, // "i"
841        ];
842        let frame = table.decode(&bytes).unwrap().0;
843        assert_eq!(
844            frame.display(false).to_string(),
845            "0.000002 INFO [Data { name: b\"Hi\" }]",
846        );
847        assert_eq!(
848            frame.display_fragments().collect::<Vec<String>>(),
849            ["[Data { name: b\"Hi\" }]"],
850        );
851    }
852
853    #[test]
854    fn display_iso8601_timestamp() {
855        let bytes = [
856            0, 0, // index
857            2, // timestamp
858            36, 188, 151, 238, 120, 1, 0, 0, // unix timestamp in bytes: 1618910624804
859        ];
860
861        decode_and_expect(
862            "{=u64:iso8601ms}",
863            &bytes,
864            "0.000002 INFO 2021-04-20T09:23:44.804Z",
865        );
866    }
867
868    #[test]
869    fn bools_simple() {
870        let bytes = [
871            0, 0,          // index
872            2,          // timestamp
873            true as u8, // the logged bool value
874        ];
875
876        decode_and_expect("my bool={=bool}", &bytes, "0.000002 INFO my bool=true");
877    }
878
879    #[test]
880    fn bitfields() {
881        let bytes = [
882            0,
883            0,           // index
884            2,           // timestamp
885            0b1110_0101, // u8
886        ];
887        decode_and_expect(
888            "x: {0=0..4:b}, y: {0=3..8:#b}",
889            &bytes,
890            "0.000002 INFO x: 101, y: 0b11100",
891        );
892    }
893
894    #[test]
895    fn bitfields_reverse_order() {
896        let bytes = [
897            0,
898            0,           // index
899            2,           // timestamp
900            0b1101_0010, // u8
901        ];
902        decode_and_expect(
903            "x: {0=0..7:b}, y: {0=3..5:b}",
904            &bytes,
905            "0.000002 INFO x: 1010010, y: 10",
906        );
907    }
908
909    #[test]
910    fn bitfields_different_indices() {
911        let bytes = [
912            0,
913            0,           // index
914            2,           // timestamp
915            0b1111_0000, // u8
916            0b1110_0101, // u8
917        ];
918        decode_and_expect(
919            "#0: {0=0..5:b}, #1: {1=3..8:b}",
920            &bytes,
921            "0.000002 INFO #0: 10000, #1: 11100",
922        );
923    }
924
925    #[test]
926    fn bitfields_u16() {
927        let bytes = [
928            0,
929            0, // index
930            2, // timestamp
931            0b1111_0000,
932            0b1110_0101, // u16
933        ];
934        decode_and_expect("x: {0=7..12:b}", &bytes, "0.000002 INFO x: 1011");
935    }
936
937    #[test]
938    fn bitfields_mixed_types() {
939        let bytes = [
940            0,
941            0, // index
942            2, // timestamp
943            0b1111_0000,
944            0b1110_0101, // u16
945            0b1111_0001, // u8
946        ];
947        decode_and_expect(
948            "#0: {0=7..12:b}, #1: {1=0..5:b}",
949            &bytes,
950            "0.000002 INFO #0: 1011, #1: 10001",
951        );
952    }
953
954    #[test]
955    fn bitfields_mixed() {
956        let bytes = [
957            0,
958            0, // index
959            2, // timestamp
960            0b1111_0000,
961            0b1110_0101, // u16 bitfields
962            42,          // u8
963            0b1111_0001, // u8 bitfields
964        ];
965        decode_and_expect(
966            "#0: {0=7..12:b}, #1: {1=u8}, #2: {2=0..5:b}",
967            &bytes,
968            "0.000002 INFO #0: 1011, #1: 42, #2: 10001",
969        );
970    }
971
972    #[test]
973    fn bitfields_across_boundaries() {
974        let bytes = [
975            0,
976            0, // index
977            2, // timestamp
978            0b1101_0010,
979            0b0110_0011, // u16
980        ];
981        decode_and_expect(
982            "bitfields {0=0..7:b} {0=9..14:b}",
983            &bytes,
984            "0.000002 INFO bitfields 1010010 10001",
985        );
986    }
987
988    #[test]
989    fn bitfields_across_boundaries_diff_indices() {
990        let bytes = [
991            0,
992            0, // index
993            2, // timestamp
994            0b1101_0010,
995            0b0110_0011, // u16
996            0b1111_1111, // truncated u16
997        ];
998        decode_and_expect(
999            "bitfields {0=0..7:b} {0=9..14:b} {1=8..10:b}",
1000            &bytes,
1001            "0.000002 INFO bitfields 1010010 10001 11",
1002        );
1003    }
1004
1005    #[test]
1006    fn bitfields_truncated_front() {
1007        let bytes = [
1008            0,
1009            0,           // index
1010            2,           // timestamp
1011            0b0110_0011, // truncated(!) u16
1012        ];
1013        decode_and_expect(
1014            "bitfields {0=9..14:b}",
1015            &bytes,
1016            "0.000002 INFO bitfields 10001",
1017        );
1018    }
1019
1020    #[test]
1021    fn bitfields_non_truncated_u32() {
1022        let bytes = [
1023            0,
1024            0,           // index
1025            2,           // timestamp
1026            0b0110_0011, // -
1027            0b0000_1111, //  |
1028            0b0101_1010, //  | u32
1029            0b1100_0011, // -
1030        ];
1031        decode_and_expect(
1032            "bitfields {0=0..2:b} {0=28..31:b}",
1033            &bytes,
1034            "0.000002 INFO bitfields 11 100",
1035        );
1036    }
1037
1038    #[test]
1039    fn bitfields_u128() {
1040        let bytes = [
1041            0,
1042            0,           // index
1043            2,           // timestamp
1044            0b1110_0101, // 120..127
1045            0b1110_0101, // 112..119
1046            0b0000_0000, // 104..111
1047            0b0000_0000, // 96..103
1048            0b0000_0000, // 88..95
1049            0b0000_0000, // 80..87
1050            0b0000_0000, // 72..79
1051            0b0000_0000, // 64..71
1052            0b0000_0000, // 56..63
1053            0b0000_0000, // 48..55
1054            0b0000_0000, // 40..47
1055            0b0000_0000, // 32..39
1056            0b0000_0000, // 24..31
1057            0b0000_0000, // 16..23
1058            0b0000_0000, // 8..15
1059            0b0000_0000, // 0..7
1060        ];
1061        decode_and_expect("x: {0=119..124:b}", &bytes, "0.000002 INFO x: 1011");
1062    }
1063
1064    #[test]
1065    fn slice() {
1066        let bytes = [
1067            0, 0, // index
1068            2, // timestamp
1069            2, 0, 0, 0, // length of the slice
1070            23, 42, // slice content
1071        ];
1072        decode_and_expect("x={=[u8]}", &bytes, "0.000002 INFO x=[23, 42]");
1073    }
1074
1075    #[test]
1076    fn slice_with_trailing_args() {
1077        let bytes = [
1078            0, 0, // index
1079            2, // timestamp
1080            2, 0, 0, 0, // length of the slice
1081            23, 42, // slice content
1082            1,  // trailing arg
1083        ];
1084
1085        decode_and_expect(
1086            "x={=[u8]} trailing arg={=u8}",
1087            &bytes,
1088            "0.000002 INFO x=[23, 42] trailing arg=1",
1089        );
1090    }
1091
1092    #[test]
1093    fn string_hello_world() {
1094        let bytes = [
1095            0, 0, // index
1096            2, // timestamp
1097            5, 0, 0, 0, // length of the string
1098            b'W', b'o', b'r', b'l', b'd',
1099        ];
1100
1101        decode_and_expect("Hello {=str}", &bytes, "0.000002 INFO Hello World");
1102    }
1103
1104    #[test]
1105    fn string_with_trailing_data() {
1106        let bytes = [
1107            0, 0, // index
1108            2, // timestamp
1109            5, 0, 0, 0, // length of the string
1110            b'W', b'o', b'r', b'l', b'd', 125, // trailing data
1111        ];
1112
1113        decode_and_expect(
1114            "Hello {=str} {=u8}",
1115            &bytes,
1116            "0.000002 INFO Hello World 125",
1117        );
1118    }
1119
1120    #[test]
1121    fn char_data() {
1122        let bytes = [
1123            0, 0, // index
1124            2, // timestamp
1125            0x61, 0x00, 0x00, 0x00, // char 'a'
1126            0x9C, 0xF4, 0x01, 0x00, // Purple heart emoji
1127        ];
1128
1129        decode_and_expect(
1130            "Supports ASCII {=char} and Unicode {=char}",
1131            &bytes,
1132            "0.000002 INFO Supports ASCII a and Unicode 💜",
1133        );
1134    }
1135
1136    #[test]
1137    fn option() {
1138        let mut entries = BTreeMap::new();
1139        entries.insert(
1140            4,
1141            TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
1142        );
1143        entries.insert(
1144            3,
1145            TableEntry::new_without_symbol(Tag::Derived, "None|Some({=?})".to_owned()),
1146        );
1147        entries.insert(
1148            2,
1149            TableEntry::new_without_symbol(Tag::Derived, "{=u8}".to_owned()),
1150        );
1151
1152        let table = Table {
1153            entries,
1154            timestamp: Some(TableEntry::new_without_symbol(
1155                Tag::Timestamp,
1156                "{=u8:us}".to_owned(),
1157            )),
1158            bitflags: Default::default(),
1159            encoding: Encoding::Raw,
1160        };
1161
1162        let bytes = [
1163            4, 0, // string index (INFO)
1164            0, // timestamp
1165            3, 0, // string index (enum)
1166            1, // Some discriminant
1167            2, 0,  // string index (u8)
1168            42, // Some.0
1169        ];
1170
1171        let frame = table.decode(&bytes).unwrap().0;
1172        assert_eq!(frame.display(false).to_string(), "0.000000 INFO x=Some(42)");
1173        assert_eq!(frame.display_message().to_string(), "x=Some(42)");
1174        assert_eq!(
1175            frame.display_fragments().collect::<Vec<String>>(),
1176            ["x=", "Some(42)"],
1177        );
1178
1179        let bytes = [
1180            4, 0, // string index (INFO)
1181            1, // timestamp
1182            3, 0, // string index (enum)
1183            0, // None discriminant
1184        ];
1185
1186        let frame = table.decode(&bytes).unwrap().0;
1187        assert_eq!(frame.display(false).to_string(), "0.000001 INFO x=None");
1188        assert_eq!(frame.display_message().to_string(), "x=None");
1189        assert_eq!(
1190            frame.display_fragments().collect::<Vec<String>>(),
1191            ["x=", "None"],
1192        );
1193    }
1194}