1#![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#[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;
28
29use crate::{decoder::Decoder, elf2table::parse_impl};
30
31pub use crate::{
32 elf2table::{Location, Locations},
33 frame::Frame,
34 stream::StreamDecoder,
35};
36
37#[derive(PartialEq, Eq, Debug)]
39pub enum Tag {
40 Prim,
42 Derived,
44 Bitflags,
46 Write,
48 Str,
50 Timestamp,
52
53 BitflagsValue,
55 Println,
57
58 Trace,
59 Debug,
60 Info,
61 Warn,
62 Error,
63}
64
65impl Tag {
66 fn to_level(&self) -> Option<Level> {
67 match self {
68 Tag::Trace => Some(Level::Trace),
69 Tag::Debug => Some(Level::Debug),
70 Tag::Info => Some(Level::Info),
71 Tag::Warn => Some(Level::Warn),
72 Tag::Error => Some(Level::Error),
73 _ => None,
74 }
75 }
76}
77
78#[derive(Debug, Eq, PartialEq)]
80pub struct TableEntry {
81 string: StringEntry,
82 raw_symbol: String,
83}
84
85impl TableEntry {
86 pub fn new(string: StringEntry, raw_symbol: String) -> Self {
87 Self { string, raw_symbol }
88 }
89
90 #[cfg(test)]
91 fn new_without_symbol(tag: Tag, string: String) -> Self {
92 Self {
93 string: StringEntry::new(tag, string),
94 raw_symbol: "<unknown>".to_string(),
95 }
96 }
97}
98
99#[derive(Debug, Eq, PartialEq)]
101pub struct StringEntry {
102 tag: Tag,
103 string: String,
104}
105
106impl StringEntry {
107 pub fn new(tag: Tag, string: String) -> Self {
108 Self { tag, string }
109 }
110}
111
112#[derive(Debug, PartialEq, Eq, Hash)]
114struct BitflagsKey {
115 ident: String,
117 package: String,
118 disambig: String,
119 crate_name: Option<String>,
120}
121
122#[derive(Copy, Clone, Debug, Eq, PartialEq)]
124#[non_exhaustive]
125pub enum Encoding {
126 Raw,
128 Rzcobs,
130}
131
132impl FromStr for Encoding {
133 type Err = anyhow::Error;
134
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 match s {
137 "raw" => Ok(Encoding::Raw),
138 "rzcobs" => Ok(Encoding::Rzcobs),
139 _ => anyhow::bail!("Unknown defmt encoding '{}' specified. This is a bug.", s),
140 }
141 }
142}
143
144impl Encoding {
145 pub const fn can_recover(&self) -> bool {
147 match self {
148 Encoding::Raw => false,
149 Encoding::Rzcobs => true,
150 }
151 }
152}
153
154#[derive(Debug, Eq, PartialEq)]
156pub struct Table {
157 timestamp: Option<TableEntry>,
158 entries: BTreeMap<usize, TableEntry>,
159 bitflags: HashMap<BitflagsKey, Vec<(String, u128)>>,
160 encoding: Encoding,
161}
162
163impl Table {
164 pub fn parse(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
168 parse_impl(elf, true)
169 }
170
171 pub fn parse_ignore_version(elf: &[u8]) -> Result<Option<Table>, anyhow::Error> {
175 parse_impl(elf, false)
176 }
177
178 pub fn set_timestamp_entry(&mut self, timestamp: TableEntry) {
179 self.timestamp = Some(timestamp);
180 }
181
182 fn _get(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
183 let entry = self.entries.get(&index).ok_or(())?;
184 Ok((entry.string.tag.to_level(), &entry.string.string))
185 }
186
187 fn get_with_level(&self, index: usize) -> Result<(Option<Level>, &str), ()> {
188 self._get(index)
189 }
190
191 fn get_without_level(&self, index: usize) -> Result<&str, ()> {
192 let (lvl, format) = self._get(index)?;
193 if lvl.is_none() {
194 Ok(format)
195 } else {
196 Err(())
197 }
198 }
199
200 pub fn indices(&self) -> impl Iterator<Item = usize> + '_ {
201 self.entries.iter().filter_map(move |(idx, entry)| {
202 if entry.string.tag.to_level().is_some() || entry.string.tag == Tag::Println {
203 Some(*idx)
204 } else {
205 None
206 }
207 })
208 }
209
210 pub fn is_empty(&self) -> bool {
211 self.entries.is_empty()
212 }
213
214 pub fn raw_symbols(&self) -> impl Iterator<Item = &str> + '_ {
216 self.entries.values().map(|s| &*s.raw_symbol)
217 }
218
219 pub fn get_locations(&self, elf: &[u8]) -> Result<Locations, anyhow::Error> {
220 elf2table::get_locations(elf, self)
221 }
222
223 pub fn decode<'t>(
229 &'t self,
230 mut bytes: &[u8],
231 ) -> Result<(Frame<'t>, usize), DecodeError> {
232 let len = bytes.len();
233 let index = bytes.read_u16::<LE>()? as u64;
234
235 let mut decoder = Decoder::new(self, bytes);
236
237 let mut timestamp_format = None;
238 let mut timestamp_args = Vec::new();
239 if let Some(entry) = self.timestamp.as_ref() {
240 let format = &entry.string.string;
241 timestamp_format = Some(&**format);
242 timestamp_args = decoder.decode_format(format)?;
243 }
244
245 let (level, format) = self
246 .get_with_level(index as usize)
247 .map_err(|_| DecodeError::Malformed)?;
248
249 let args = decoder.decode_format(format)?;
250
251 let frame = Frame::new(
252 self,
253 level,
254 index,
255 timestamp_format,
256 timestamp_args,
257 format,
258 args,
259 );
260
261 let consumed = len - decoder.bytes.len();
262 Ok((frame, consumed))
263 }
264
265 pub fn new_stream_decoder(&self) -> Box<dyn StreamDecoder + '_> {
266 match self.encoding {
267 Encoding::Raw => Box::new(stream::Raw::new(self)),
268 Encoding::Rzcobs => Box::new(stream::Rzcobs::new(self)),
269 }
270 }
271
272 pub fn encoding(&self) -> Encoding {
273 self.encoding
274 }
275
276 pub fn has_timestamp(&self) -> bool {
277 self.timestamp.is_some()
278 }
279}
280
281#[derive(Debug, Clone, PartialEq)]
283enum Arg<'t> {
284 Bool(bool),
286 F32(f32),
287 F64(f64),
288 Uxx(u128),
290 Ixx(i128),
292 Str(String),
294 IStr(&'t str),
296 Format {
298 format: &'t str,
299 args: Vec<Arg<'t>>,
300 },
301 FormatSlice {
302 elements: Vec<FormatSliceElement<'t>>,
303 },
304 FormatSequence {
305 args: Vec<Arg<'t>>,
306 },
307 Slice(Vec<u8>),
309 Char(char),
311
312 Preformatted(String),
314}
315
316#[derive(Debug, Clone, PartialEq)]
317struct FormatSliceElement<'t> {
318 format: &'t str,
321 args: Vec<Arg<'t>>,
322}
323
324#[derive(Debug, Eq, PartialEq)]
326pub enum DecodeError {
327 UnexpectedEof,
329 Malformed,
331}
332
333impl From<io::Error> for DecodeError {
334 fn from(e: io::Error) -> Self {
335 if e.kind() == io::ErrorKind::UnexpectedEof {
336 DecodeError::UnexpectedEof
337 } else {
338 DecodeError::Malformed
339 }
340 }
341}
342
343impl fmt::Display for DecodeError {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 match self {
346 DecodeError::UnexpectedEof => f.write_str("unexpected end of stream"),
347 DecodeError::Malformed => f.write_str("malformed data"),
348 }
349 }
350}
351
352impl Error for DecodeError {}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 fn test_table(entries: impl IntoIterator<Item = TableEntry>) -> Table {
359 Table {
360 timestamp: None,
361 entries: entries.into_iter().enumerate().collect(),
362 bitflags: Default::default(),
363 encoding: Encoding::Raw,
364 }
365 }
366
367 fn test_table_with_timestamp(
368 entries: impl IntoIterator<Item = TableEntry>,
369 timestamp: &str,
370 ) -> Table {
371 Table {
372 timestamp: Some(TableEntry::new_without_symbol(
373 Tag::Timestamp,
374 timestamp.into(),
375 )),
376 entries: entries.into_iter().enumerate().collect(),
377 bitflags: Default::default(),
378 encoding: Encoding::Raw,
379 }
380 }
381
382 fn decode_and_expect(format: &str, bytes: &[u8], expectation: &str) {
388 let mut entries = BTreeMap::new();
389 entries.insert(
390 bytes[0] as usize,
391 TableEntry::new_without_symbol(Tag::Info, format.to_string()),
392 );
393
394 let table = Table {
395 entries,
396 timestamp: Some(TableEntry::new_without_symbol(
397 Tag::Timestamp,
398 "{=u8:us}".to_owned(),
399 )),
400 bitflags: Default::default(),
401 encoding: Encoding::Raw,
402 };
403
404 let frame = table.decode(bytes).unwrap().0;
405 assert_eq!(frame.display(false).to_string(), expectation.to_owned());
406 }
407
408 #[test]
409 fn decode() {
410 let entries = vec![
411 TableEntry::new_without_symbol(Tag::Info, "Hello, world!".to_owned()),
412 TableEntry::new_without_symbol(Tag::Debug, "The answer is {=u8}!".to_owned()),
413 ];
414
415 let table = test_table(entries);
416
417 let bytes = [0, 0];
418 assert_eq!(
421 table.decode(&bytes),
422 Ok((
423 Frame::new(
424 &table,
425 Some(Level::Info),
426 0,
427 None,
428 vec![],
429 "Hello, world!",
430 vec![],
431 ),
432 bytes.len(),
433 ))
434 );
435
436 let bytes = [
437 1, 0, 42, ];
440
441 assert_eq!(
442 table.decode(&bytes),
443 Ok((
444 Frame::new(
445 &table,
446 Some(Level::Debug),
447 1,
448 None,
449 vec![],
450 "The answer is {=u8}!",
451 vec![Arg::Uxx(42)],
452 ),
453 bytes.len(),
454 ))
455 );
456
457 }
459
460 #[test]
461 fn all_integers() {
462 const FMT: &str =
463 "Hello, {=u8} {=u16} {=u32} {=u64} {=u128} {=i8} {=i16} {=i32} {=i64} {=i128}!";
464
465 let entries = vec![TableEntry::new_without_symbol(Tag::Info, FMT.to_owned())];
466
467 let table = test_table(entries);
468
469 let bytes = [
470 0, 0, 42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
476 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
482 0xff, 0xff, ];
484
485 assert_eq!(
486 table.decode(&bytes),
487 Ok((
488 Frame::new(
489 &table,
490 Some(Level::Info),
491 0,
492 None,
493 vec![],
494 FMT,
495 vec![
496 Arg::Uxx(42), Arg::Uxx(u16::max_value().into()), Arg::Uxx(u32::max_value().into()), Arg::Uxx(u64::max_value().into()), Arg::Uxx(u128::max_value()), Arg::Ixx(-1), Arg::Ixx(-1), Arg::Ixx(-1), Arg::Ixx(-1), Arg::Ixx(-1), ],
507 ),
508 bytes.len(),
509 ))
510 );
511 }
512
513 #[test]
514 fn indices() {
515 let entries = vec![
516 TableEntry::new_without_symbol(Tag::Info, "The answer is {0=u8} {0=u8}!".to_owned()),
517 TableEntry::new_without_symbol(
518 Tag::Info,
519 "The answer is {1=u16} {0=u8} {1=u16}!".to_owned(),
520 ),
521 ];
522
523 let table = test_table(entries);
524 let bytes = [
525 0, 0, 42, ];
528
529 assert_eq!(
530 table.decode(&bytes),
531 Ok((
532 Frame::new(
533 &table,
534 Some(Level::Info),
535 0,
536 None,
537 vec![],
538 "The answer is {0=u8} {0=u8}!",
539 vec![Arg::Uxx(42)],
540 ),
541 bytes.len(),
542 ))
543 );
544
545 let bytes = [
546 1, 0, 42, 0xff, 0xff, ];
550
551 assert_eq!(
552 table.decode(&bytes),
553 Ok((
554 Frame::new(
555 &table,
556 Some(Level::Info),
557 1,
558 None,
559 vec![],
560 "The answer is {1=u16} {0=u8} {1=u16}!",
561 vec![Arg::Uxx(42), Arg::Uxx(0xffff)],
562 ),
563 bytes.len(),
564 ))
565 );
566 }
567
568 #[test]
569 fn format() {
570 let entries = vec![
571 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
572 TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
573 ];
574
575 let table = test_table(entries);
576
577 let bytes = [
578 0, 0, 1, 0, 42, ];
582
583 assert_eq!(
584 table.decode(&bytes),
585 Ok((
586 Frame::new(
587 &table,
588 Some(Level::Info),
589 0,
590 None,
591 vec![],
592 "x={=?}",
593 vec![Arg::Format {
594 format: "Foo {{ x: {=u8} }}",
595 args: vec![Arg::Uxx(42)]
596 }],
597 ),
598 bytes.len(),
599 ))
600 );
601 }
602
603 #[test]
604 fn format_sequence() {
605 let entries = vec![
606 TableEntry::new_without_symbol(Tag::Info, "{=__internal_FormatSequence}".to_owned()),
607 TableEntry::new_without_symbol(Tag::Derived, "Foo".to_owned()),
608 TableEntry::new_without_symbol(Tag::Derived, "Bar({=u8})".to_owned()),
609 TableEntry::new_without_symbol(Tag::Derived, "State {=u8}|".to_owned()),
610 ];
611
612 let table = test_table(entries);
613
614 let bytes = [
615 0, 0, 1, 0, 2, 0, 42, 3, 0, 23, 0, 0, ];
623
624 assert_eq!(
625 table.decode(&bytes),
626 Ok((
627 Frame::new(
628 &table,
629 Some(Level::Info),
630 0,
631 None,
632 vec![],
633 "{=__internal_FormatSequence}",
634 vec![Arg::FormatSequence {
635 args: vec![
636 Arg::Format {
637 format: "Foo",
638 args: vec![]
639 },
640 Arg::Format {
641 format: "Bar({=u8})",
642 args: vec![Arg::Uxx(42)]
643 },
644 Arg::Format {
645 format: "State {=u8}|",
646 args: vec![Arg::Uxx(23)]
647 }
648 ]
649 }],
650 ),
651 bytes.len(),
652 ))
653 );
654 }
655
656 #[test]
657 fn display() {
658 let entries = vec![
659 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
660 TableEntry::new_without_symbol(Tag::Derived, "Foo {{ x: {=u8} }}".to_owned()),
661 ];
662
663 let table = test_table_with_timestamp(entries, "{=u8:us}");
664
665 let bytes = [
666 0, 0, 2, 1, 0, 42, ];
671
672 let frame = table.decode(&bytes).unwrap().0;
673 assert_eq!(
674 frame.display(false).to_string(),
675 "0.000002 INFO x=Foo { x: 42 }"
676 );
677 }
678
679 #[test]
680 fn display_i16_with_hex_hint() {
681 let bytes = [
683 0,
684 0, 2, 0b1111_1111, 0b1111_1111,
688 ];
689
690 decode_and_expect(
691 "i16 as hex {=i16:#x}",
692 &bytes,
693 "0.000002 INFO i16 as hex 0xffff",
694 );
695 }
696
697 #[test]
698 fn display_use_inner_type_hint() {
699 let entries = vec![
700 TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
701 TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:x} }}".to_owned()),
702 ];
703
704 let table = test_table_with_timestamp(entries, "{=u8:us}");
705
706 let bytes = [
707 0, 0, 2, 1, 0, 42, ];
712
713 let frame = table.decode(&bytes).unwrap().0;
714 assert_eq!(
715 frame.display(false).to_string(),
716 "0.000002 INFO x=S { x: 2a }",
717 );
718 }
719
720 #[test]
721 fn display_use_outer_type_hint() {
722 let entries = vec![
723 TableEntry::new_without_symbol(Tag::Info, "x={:b}".to_owned()),
724 TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=u8:?} }}".to_owned()),
725 ];
726
727 let table = test_table_with_timestamp(entries, "{=u8:us}");
728
729 let bytes = [
730 0, 0, 2, 1, 0, 42, ];
735
736 let frame = table.decode(&bytes).unwrap().0;
737 assert_eq!(
738 frame.display(false).to_string(),
739 "0.000002 INFO x=S { x: 101010 }",
740 );
741 }
742
743 #[test]
744 fn display_inner_str_in_struct() {
745 let entries = vec![
746 TableEntry::new_without_symbol(Tag::Info, "{}".to_owned()),
747 TableEntry::new_without_symbol(Tag::Derived, "S {{ x: {=str:?} }}".to_owned()),
748 ];
749
750 let table = test_table_with_timestamp(entries, "{=u8:us}");
751
752 let bytes = [
753 0, 0, 2, 1, 0, 5, 0, 0, 0, b'H', b'e', b'l', b'l', b'o', ];
759 let frame = table.decode(&bytes).unwrap().0;
760 assert_eq!(
761 frame.display(false).to_string(),
762 "0.000002 INFO S { x: \"Hello\" }",
763 );
764 }
765
766 #[test]
767 fn display_u8_vec() {
768 let entries = vec![
769 TableEntry::new_without_symbol(Tag::Prim, "{=u8}".to_owned()),
770 TableEntry::new_without_symbol(Tag::Prim, "{=[?]}".to_owned()),
771 TableEntry::new_without_symbol(Tag::Derived, "Data {{ name: {=?:?} }}".to_owned()),
772 TableEntry::new_without_symbol(Tag::Info, "{=[?]:a}".to_owned()),
773 ];
774
775 let table = test_table_with_timestamp(entries, "{=u8:us}");
776
777 let bytes = [
778 3, 0, 2, 1, 0, 0, 0, 2, 0, 1, 0, 2, 0, 0, 0, 0, 0, 72, 105, ];
788 let frame = table.decode(&bytes).unwrap().0;
789 assert_eq!(
790 frame.display(false).to_string(),
791 "0.000002 INFO [Data { name: b\"Hi\" }]",
792 );
793 }
794
795 #[test]
796 fn display_iso8601_timestamp() {
797 let bytes = [
798 0, 0, 2, 36, 188, 151, 238, 120, 1, 0, 0, ];
802
803 decode_and_expect(
804 "{=u64:iso8601ms}",
805 &bytes,
806 "0.000002 INFO 2021-04-20T09:23:44.804Z",
807 );
808 }
809
810 #[test]
811 fn bools_simple() {
812 let bytes = [
813 0, 0, 2, true as u8, ];
817
818 decode_and_expect("my bool={=bool}", &bytes, "0.000002 INFO my bool=true");
819 }
820
821 #[test]
822 fn bitfields() {
823 let bytes = [
824 0,
825 0, 2, 0b1110_0101, ];
829 decode_and_expect(
830 "x: {0=0..4:b}, y: {0=3..8:#b}",
831 &bytes,
832 "0.000002 INFO x: 101, y: 0b11100",
833 );
834 }
835
836 #[test]
837 fn bitfields_reverse_order() {
838 let bytes = [
839 0,
840 0, 2, 0b1101_0010, ];
844 decode_and_expect(
845 "x: {0=0..7:b}, y: {0=3..5:b}",
846 &bytes,
847 "0.000002 INFO x: 1010010, y: 10",
848 );
849 }
850
851 #[test]
852 fn bitfields_different_indices() {
853 let bytes = [
854 0,
855 0, 2, 0b1111_0000, 0b1110_0101, ];
860 decode_and_expect(
861 "#0: {0=0..5:b}, #1: {1=3..8:b}",
862 &bytes,
863 "0.000002 INFO #0: 10000, #1: 11100",
864 );
865 }
866
867 #[test]
868 fn bitfields_u16() {
869 let bytes = [
870 0,
871 0, 2, 0b1111_0000,
874 0b1110_0101, ];
876 decode_and_expect("x: {0=7..12:b}", &bytes, "0.000002 INFO x: 1011");
877 }
878
879 #[test]
880 fn bitfields_mixed_types() {
881 let bytes = [
882 0,
883 0, 2, 0b1111_0000,
886 0b1110_0101, 0b1111_0001, ];
889 decode_and_expect(
890 "#0: {0=7..12:b}, #1: {1=0..5:b}",
891 &bytes,
892 "0.000002 INFO #0: 1011, #1: 10001",
893 );
894 }
895
896 #[test]
897 fn bitfields_mixed() {
898 let bytes = [
899 0,
900 0, 2, 0b1111_0000,
903 0b1110_0101, 42, 0b1111_0001, ];
907 decode_and_expect(
908 "#0: {0=7..12:b}, #1: {1=u8}, #2: {2=0..5:b}",
909 &bytes,
910 "0.000002 INFO #0: 1011, #1: 42, #2: 10001",
911 );
912 }
913
914 #[test]
915 fn bitfields_across_boundaries() {
916 let bytes = [
917 0,
918 0, 2, 0b1101_0010,
921 0b0110_0011, ];
923 decode_and_expect(
924 "bitfields {0=0..7:b} {0=9..14:b}",
925 &bytes,
926 "0.000002 INFO bitfields 1010010 10001",
927 );
928 }
929
930 #[test]
931 fn bitfields_across_boundaries_diff_indices() {
932 let bytes = [
933 0,
934 0, 2, 0b1101_0010,
937 0b0110_0011, 0b1111_1111, ];
940 decode_and_expect(
941 "bitfields {0=0..7:b} {0=9..14:b} {1=8..10:b}",
942 &bytes,
943 "0.000002 INFO bitfields 1010010 10001 11",
944 );
945 }
946
947 #[test]
948 fn bitfields_truncated_front() {
949 let bytes = [
950 0,
951 0, 2, 0b0110_0011, ];
955 decode_and_expect(
956 "bitfields {0=9..14:b}",
957 &bytes,
958 "0.000002 INFO bitfields 10001",
959 );
960 }
961
962 #[test]
963 fn bitfields_non_truncated_u32() {
964 let bytes = [
965 0,
966 0, 2, 0b0110_0011, 0b0000_1111, 0b0101_1010, 0b1100_0011, ];
973 decode_and_expect(
974 "bitfields {0=0..2:b} {0=28..31:b}",
975 &bytes,
976 "0.000002 INFO bitfields 11 100",
977 );
978 }
979
980 #[test]
981 fn bitfields_u128() {
982 let bytes = [
983 0,
984 0, 2, 0b1110_0101, 0b1110_0101, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, ];
1003 decode_and_expect("x: {0=119..124:b}", &bytes, "0.000002 INFO x: 1011");
1004 }
1005
1006 #[test]
1007 fn slice() {
1008 let bytes = [
1009 0, 0, 2, 2, 0, 0, 0, 23, 42, ];
1014 decode_and_expect("x={=[u8]}", &bytes, "0.000002 INFO x=[23, 42]");
1015 }
1016
1017 #[test]
1018 fn slice_with_trailing_args() {
1019 let bytes = [
1020 0, 0, 2, 2, 0, 0, 0, 23, 42, 1, ];
1026
1027 decode_and_expect(
1028 "x={=[u8]} trailing arg={=u8}",
1029 &bytes,
1030 "0.000002 INFO x=[23, 42] trailing arg=1",
1031 );
1032 }
1033
1034 #[test]
1035 fn string_hello_world() {
1036 let bytes = [
1037 0, 0, 2, 5, 0, 0, 0, b'W', b'o', b'r', b'l', b'd',
1041 ];
1042
1043 decode_and_expect("Hello {=str}", &bytes, "0.000002 INFO Hello World");
1044 }
1045
1046 #[test]
1047 fn string_with_trailing_data() {
1048 let bytes = [
1049 0, 0, 2, 5, 0, 0, 0, b'W', b'o', b'r', b'l', b'd', 125, ];
1054
1055 decode_and_expect(
1056 "Hello {=str} {=u8}",
1057 &bytes,
1058 "0.000002 INFO Hello World 125",
1059 );
1060 }
1061
1062 #[test]
1063 fn char_data() {
1064 let bytes = [
1065 0, 0, 2, 0x61, 0x00, 0x00, 0x00, 0x9C, 0xF4, 0x01, 0x00, ];
1070
1071 decode_and_expect(
1072 "Supports ASCII {=char} and Unicode {=char}",
1073 &bytes,
1074 "0.000002 INFO Supports ASCII a and Unicode 💜",
1075 );
1076 }
1077
1078 #[test]
1079 fn option() {
1080 let mut entries = BTreeMap::new();
1081 entries.insert(
1082 4,
1083 TableEntry::new_without_symbol(Tag::Info, "x={=?}".to_owned()),
1084 );
1085 entries.insert(
1086 3,
1087 TableEntry::new_without_symbol(Tag::Derived, "None|Some({=?})".to_owned()),
1088 );
1089 entries.insert(
1090 2,
1091 TableEntry::new_without_symbol(Tag::Derived, "{=u8}".to_owned()),
1092 );
1093
1094 let table = Table {
1095 entries,
1096 timestamp: Some(TableEntry::new_without_symbol(
1097 Tag::Timestamp,
1098 "{=u8:us}".to_owned(),
1099 )),
1100 bitflags: Default::default(),
1101 encoding: Encoding::Raw,
1102 };
1103
1104 let bytes = [
1105 4, 0, 0, 3, 0, 1, 2, 0, 42, ];
1112
1113 let frame = table.decode(&bytes).unwrap().0;
1114 assert_eq!(frame.display(false).to_string(), "0.000000 INFO x=Some(42)");
1115
1116 let bytes = [
1117 4, 0, 1, 3, 0, 0, ];
1122
1123 let frame = table.decode(&bytes).unwrap().0;
1124 assert_eq!(frame.display(false).to_string(), "0.000001 INFO x=None");
1125 }
1126}