Skip to main content

defmt_decoder/
frame.rs

1use std::{
2    convert::TryFrom,
3    fmt::{self, Write as _},
4    mem,
5};
6
7use crate::{Arg, BitflagsKey, Table};
8use colored::Colorize;
9use defmt_parser::{DisplayHint, Fragment, Level, ParserMode, TimePrecision, Type};
10use time::{macros::format_description, OffsetDateTime};
11
12/// Used to convert a `i128` value into right target type in hex
13struct I128Hex(i128, Type);
14
15impl std::fmt::LowerHex for I128Hex {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        match self.1 {
18            Type::I8 => fmt::LowerHex::fmt(&(self.0 as i8), f),
19            Type::I16 => fmt::LowerHex::fmt(&(self.0 as i16), f),
20            Type::I32 => fmt::LowerHex::fmt(&(self.0 as i32), f),
21            Type::I64 => fmt::LowerHex::fmt(&(self.0 as i64), f),
22            Type::I128 => fmt::LowerHex::fmt(&self.0, f),
23            _ => panic!("Unsupported type '{:?}' found.", self.1),
24        }
25    }
26}
27
28impl std::fmt::UpperHex for I128Hex {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self.1 {
31            Type::I8 => fmt::UpperHex::fmt(&(self.0 as i8), f),
32            Type::I16 => fmt::UpperHex::fmt(&(self.0 as i16), f),
33            Type::I32 => fmt::UpperHex::fmt(&(self.0 as i32), f),
34            Type::I64 => fmt::UpperHex::fmt(&(self.0 as i64), f),
35            Type::I128 => fmt::UpperHex::fmt(&self.0, f),
36            _ => panic!("Unsupported type '{:?}' found.", self.1),
37        }
38    }
39}
40
41/// A log frame
42#[derive(Debug, PartialEq)]
43pub struct Frame<'t> {
44    table: &'t Table,
45    level: Option<Level>,
46    index: u64,
47    timestamp_format: Option<&'t str>,
48    timestamp_args: Vec<Arg<'t>>,
49    // Format string
50    format: &'t str,
51    args: Vec<Arg<'t>>,
52}
53
54impl<'t> Frame<'t> {
55    pub(crate) fn new(
56        table: &'t Table,
57        level: Option<Level>,
58        index: u64,
59        timestamp_format: Option<&'t str>,
60        timestamp_args: Vec<Arg<'t>>,
61        format: &'t str,
62        args: Vec<Arg<'t>>,
63    ) -> Self {
64        Self {
65            table,
66            level,
67            index,
68            timestamp_format,
69            timestamp_args,
70            format,
71            args,
72        }
73    }
74
75    /// Returns a struct that will format this log frame (including message, timestamp, level,
76    /// etc.).
77    pub fn display(&'t self, colored: bool) -> DisplayFrame<'t> {
78        DisplayFrame {
79            frame: self,
80            colored,
81        }
82    }
83
84    pub fn display_timestamp(&'t self) -> Option<DisplayTimestamp<'t>> {
85        self.timestamp_format
86            .map(|_| DisplayTimestamp { frame: self })
87    }
88
89    /// Returns a struct that will format the message contained in this log frame.
90    pub fn display_message(&'t self) -> DisplayMessage<'t> {
91        DisplayMessage { frame: self }
92    }
93
94    /// Returns an iterator over the fragments of the message contained in this log frame.
95    ///
96    /// Collecting this into a String will yield the same result as [`Self::display_message`], but
97    /// this iterator will yield interpolated fragments on their own. For example, the log:
98    ///
99    /// ```ignore
100    /// defmt::info!("foo = {}, bar = {}", 1, 2);
101    /// ```
102    ///
103    /// Will yield the following strings:
104    ///
105    /// ```ignore
106    /// vec!["foo = ", "1", ", bar = ", "2"]
107    /// ```
108    ///
109    /// Note that nested fragments will not yield separately:
110    ///
111    /// ```ignore
112    /// defmt::info!("foo = {}", Foo { bar: 1 });
113    /// ```
114    ///
115    /// Will yield:
116    ///
117    /// ```ignore
118    /// vec!["foo = ", "Foo { bar: 1 }"]
119    /// ```
120    ///
121    /// This iterator yields the same fragments as [`Self::fragments`], so you can zip them
122    /// together to get both representations.
123    pub fn display_fragments(&'t self) -> DisplayFragments<'t> {
124        DisplayFragments {
125            frame: self,
126            iter: self.fragments().into_iter(),
127        }
128    }
129
130    /// Returns the fragments of the message contained in this log frame.
131    ///
132    /// Each fragment represents a part of the log message. See [`Fragment`] for more details.
133    ///
134    /// This iterator yields the same fragments as [`Self::display_fragments`], so you can zip them
135    /// together to get both representations.
136    pub fn fragments(&'t self) -> Vec<Fragment<'t>> {
137        defmt_parser::parse(self.format, ParserMode::ForwardsCompatible).unwrap()
138    }
139
140    pub fn level(&self) -> Option<Level> {
141        self.level
142    }
143
144    pub fn index(&self) -> u64 {
145        self.index
146    }
147
148    fn format_args(&self, format: &str, args: &[Arg], parent_hint: Option<&DisplayHint>) -> String {
149        let params = defmt_parser::parse(format, ParserMode::ForwardsCompatible).unwrap();
150        let mut buf = String::new();
151        for param in params {
152            self.format_fragment(param, &mut buf, args, parent_hint)
153                .unwrap(); // cannot fail, we only write to a `String`
154        }
155        buf
156    }
157
158    fn format_fragment(
159        &self,
160        param: Fragment<'_>,
161        buf: &mut String,
162        args: &[Arg],
163        parent_hint: Option<&DisplayHint>,
164    ) -> Result<(), fmt::Error> {
165        match param {
166            Fragment::Literal(lit) => {
167                buf.push_str(&lit);
168            }
169            Fragment::Parameter(param) => {
170                let hint = param.hint.as_ref().or(parent_hint);
171
172                match &args[param.index] {
173                    Arg::Bool(x) => write!(buf, "{x}")?,
174                    Arg::F32(x) => write!(buf, "{}", ryu::Buffer::new().format(*x))?,
175                    Arg::F64(x) => write!(buf, "{}", ryu::Buffer::new().format(*x))?,
176                    Arg::Uxx(x) => {
177                        match param.ty {
178                            Type::BitField(range) => {
179                                let left_zeroes = mem::size_of::<u128>() * 8 - range.end as usize;
180                                let right_zeroes = left_zeroes + range.start as usize;
181                                // isolate the desired bitfields
182                                let bitfields = (*x << left_zeroes) >> right_zeroes;
183
184                                if let Some(DisplayHint::Ascii) = hint {
185                                    let bstr = bitfields
186                                        .to_be_bytes()
187                                        .iter()
188                                        .skip(right_zeroes / 8)
189                                        .copied()
190                                        .collect::<Vec<u8>>();
191                                    self.format_bytes(&bstr, hint, buf)?
192                                } else {
193                                    self.format_u128(bitfields, hint, buf)?;
194                                }
195                            }
196                            _ => match hint {
197                                Some(DisplayHint::ISO8601(precision)) => {
198                                    self.format_iso8601(*x as u64, precision, buf)?
199                                }
200                                Some(DisplayHint::Debug) => {
201                                    self.format_u128(*x, parent_hint, buf)?
202                                }
203                                _ => self.format_u128(*x, hint, buf)?,
204                            },
205                        }
206                    }
207                    Arg::Ixx(x) => self.format_i128(*x, param.ty, hint, buf)?,
208                    Arg::Str(x) | Arg::Preformatted(x) => self.format_str(x, hint, buf)?,
209                    Arg::IStr(x) => self.format_str(x, hint, buf)?,
210                    Arg::Format { format, args } => match parent_hint {
211                        Some(DisplayHint::Ascii) => {
212                            buf.push_str(&self.format_args(format, args, parent_hint));
213                        }
214                        _ => buf.push_str(&self.format_args(format, args, hint)),
215                    },
216                    Arg::FormatSequence { args } => {
217                        for arg in args {
218                            buf.push_str(&self.format_args("{=?}", &[arg.clone()], hint))
219                        }
220                    }
221                    Arg::FormatSlice { elements } => {
222                        match hint {
223                            // Filter Ascii Hints, which contains u8 byte slices
224                            Some(DisplayHint::Ascii)
225                                if elements.iter().filter(|e| e.format == "{=u8}").count() != 0 =>
226                            {
227                                let vals = elements
228                                    .iter()
229                                    .map(|e| match e.args.as_slice() {
230                                        [Arg::Uxx(v)] => {
231                                            u8::try_from(*v).expect("the value must be in u8 range")
232                                        }
233                                        _ => panic!("FormatSlice should only contain one argument"),
234                                    })
235                                    .collect::<Vec<u8>>();
236                                self.format_bytes(&vals, hint, buf)?
237                            }
238                            _ => {
239                                buf.write_str("[")?;
240                                let mut is_first = true;
241                                for element in elements {
242                                    if !is_first {
243                                        buf.write_str(", ")?;
244                                    }
245                                    is_first = false;
246                                    buf.write_str(&self.format_args(
247                                        element.format,
248                                        &element.args,
249                                        hint,
250                                    ))?;
251                                }
252                                buf.write_str("]")?;
253                            }
254                        }
255                    }
256                    Arg::Slice(x) => self.format_bytes(x, hint, buf)?,
257                    Arg::Char(c) => write!(buf, "{c}")?,
258                }
259            }
260        }
261
262        Ok(())
263    }
264
265    fn format_u128(
266        &self,
267        x: u128,
268        hint: Option<&DisplayHint>,
269        buf: &mut String,
270    ) -> Result<(), fmt::Error> {
271        match hint {
272            Some(DisplayHint::NoHint { zero_pad }) => write!(buf, "{x:0zero_pad$}")?,
273            Some(DisplayHint::Binary {
274                alternate,
275                zero_pad,
276            }) => match alternate {
277                true => write!(buf, "{x:#0zero_pad$b}")?,
278                false => write!(buf, "{x:0zero_pad$b}")?,
279            },
280            Some(DisplayHint::Octal {
281                alternate,
282                zero_pad,
283            }) => match alternate {
284                true => write!(buf, "{x:#0zero_pad$o}")?,
285                false => write!(buf, "{x:0zero_pad$o}")?,
286            },
287            Some(DisplayHint::Hexadecimal {
288                uppercase,
289                alternate,
290                zero_pad,
291            }) => match (alternate, uppercase) {
292                (false, false) => write!(buf, "{x:0zero_pad$x}")?,
293                (false, true) => write!(buf, "{x:0zero_pad$X}")?,
294                (true, false) => write!(buf, "{x:#0zero_pad$x}")?,
295                (true, true) => write!(buf, "{x:#0zero_pad$X}")?,
296            },
297            Some(DisplayHint::Seconds(TimePrecision::Micros)) => {
298                let seconds = x / 1_000_000;
299                let micros = x % 1_000_000;
300                write!(buf, "{seconds}.{micros:06}")?;
301            }
302            Some(DisplayHint::Seconds(TimePrecision::Millis)) => {
303                let seconds = x / 1_000;
304                let millis = x % 1_000;
305                write!(buf, "{seconds}.{millis:03}")?;
306            }
307            Some(DisplayHint::Time(TimePrecision::Micros)) => {
308                self.format_time(x, &TimePrecision::Micros, buf)?;
309            }
310            Some(DisplayHint::Time(TimePrecision::Millis)) => {
311                self.format_time(x, &TimePrecision::Millis, buf)?;
312            }
313            Some(DisplayHint::Time(TimePrecision::Seconds)) => {
314                self.format_time(x, &TimePrecision::Seconds, buf)?;
315            }
316            Some(DisplayHint::Bitflags {
317                name,
318                package,
319                disambiguator,
320                crate_name,
321            }) => {
322                // The bitflags hint is only used internally, in `Format` impls generated by
323                // `defmt::bitflags!`.
324                let key = BitflagsKey {
325                    ident: name.clone(),
326                    package: package.clone(),
327                    disambig: disambiguator.clone(),
328                    crate_name: crate_name.clone(),
329                };
330                match self.table.bitflags.get(&key) {
331                    Some(flags) => {
332                        let set_flags = flags
333                            .iter()
334                            .filter(|(_, value)| {
335                                if *value == 0 && x != 0 {
336                                    false
337                                } else {
338                                    x & value == *value
339                                }
340                            })
341                            .map(|(name, _)| name.clone())
342                            .collect::<Vec<_>>();
343                        if set_flags.is_empty() {
344                            write!(buf, "(empty)")?;
345                        } else {
346                            write!(buf, "{}", set_flags.join(" | "))?;
347                        }
348                    }
349                    None => {
350                        // FIXME return an internal error here
351                        write!(buf, "{x}")?;
352                    }
353                }
354            }
355            _ => write!(buf, "{x}")?,
356        }
357        Ok(())
358    }
359
360    fn format_i128(
361        &self,
362        x: i128,
363        ty: Type,
364        hint: Option<&DisplayHint>,
365        buf: &mut String,
366    ) -> Result<(), fmt::Error> {
367        match hint {
368            Some(DisplayHint::NoHint { zero_pad }) => write!(buf, "{x:0zero_pad$}")?,
369            Some(DisplayHint::Binary {
370                alternate,
371                zero_pad,
372            }) => match alternate {
373                true => write!(buf, "{x:#0zero_pad$b}")?,
374                false => write!(buf, "{x:0zero_pad$b}")?,
375            },
376            Some(DisplayHint::Octal {
377                alternate,
378                zero_pad,
379            }) => match alternate {
380                true => write!(buf, "{x:#0zero_pad$o}")?,
381                false => write!(buf, "{x:0zero_pad$o}")?,
382            },
383            Some(DisplayHint::Hexadecimal {
384                uppercase,
385                alternate,
386                zero_pad,
387            }) => {
388                let value = I128Hex(x, ty);
389                match (alternate, uppercase) {
390                    (false, false) => write!(buf, "{value:0zero_pad$x}")?,
391                    (false, true) => write!(buf, "{value:0zero_pad$X}")?,
392                    (true, false) => write!(buf, "{value:#0zero_pad$x}")?,
393                    (true, true) => write!(buf, "{value:#0zero_pad$X}")?,
394                }
395            }
396            _ => write!(buf, "{x}")?,
397        }
398        Ok(())
399    }
400
401    fn format_bytes(
402        &self,
403        bytes: &[u8],
404        hint: Option<&DisplayHint>,
405        buf: &mut String,
406    ) -> Result<(), fmt::Error> {
407        match hint {
408            Some(DisplayHint::Ascii) => {
409                // byte string literal syntax: b"Hello\xffworld"
410                buf.push_str("b\"");
411                for byte in bytes {
412                    match byte {
413                        // special escaping
414                        b'\t' => buf.push_str("\\t"),
415                        b'\n' => buf.push_str("\\n"),
416                        b'\r' => buf.push_str("\\r"),
417                        b' ' => buf.push(' '),
418                        b'\"' => buf.push_str("\\\""),
419                        b'\\' => buf.push_str("\\\\"),
420                        _ => {
421                            if byte.is_ascii_graphic() {
422                                buf.push(*byte as char);
423                            } else {
424                                // general escaped form
425                                write!(buf, "\\x{byte:02x}").ok();
426                            }
427                        }
428                    }
429                }
430                buf.push('\"');
431            }
432            Some(DisplayHint::Hexadecimal { .. })
433            | Some(DisplayHint::Octal { .. })
434            | Some(DisplayHint::Binary { .. }) => {
435                // `core::write!` doesn't quite produce the output we want, for example
436                // `write!("{:#04x?}", bytes)` produces a multi-line output
437                // `write!("{:02x?}", bytes)` is single-line but each byte doesn't include the "0x" prefix
438                buf.push('[');
439                let mut is_first = true;
440                for byte in bytes {
441                    if !is_first {
442                        buf.push_str(", ");
443                    }
444                    is_first = false;
445                    self.format_u128(*byte as u128, hint, buf)?;
446                }
447                buf.push(']');
448            }
449            Some(DisplayHint::Cbor) => {
450                use core::fmt::Write;
451                let parsed = cbor_edn::Sequence::from_cbor(bytes);
452                match parsed {
453                    Ok(parsed) => buf.write_str(&parsed.serialize())?,
454                    Err(err) => {
455                        write!(buf, "invalid CBOR (error: {}, bytes: {:02x?})", err, bytes)?
456                    }
457                }
458            }
459            _ => write!(buf, "{bytes:?}")?,
460        }
461        Ok(())
462    }
463
464    fn format_str(
465        &self,
466        s: &str,
467        hint: Option<&DisplayHint>,
468        buf: &mut String,
469    ) -> Result<(), fmt::Error> {
470        if hint == Some(&DisplayHint::Debug) {
471            write!(buf, "{s:?}")?;
472        } else {
473            buf.push_str(s);
474        }
475        Ok(())
476    }
477
478    fn format_time(
479        &self,
480        timestamp: u128,
481        precision: &TimePrecision,
482        buf: &mut String,
483    ) -> Result<(), fmt::Error> {
484        let div_rem = |x, y| (x / y, x % y);
485
486        let (timestamp, decimals) = match precision {
487            TimePrecision::Micros => div_rem(timestamp, 1_000_000),
488            TimePrecision::Millis => div_rem(timestamp, 1_000),
489            TimePrecision::Seconds => (timestamp, 0),
490        };
491
492        let (timestamp, seconds) = div_rem(timestamp, 60);
493        let (timestamp, minutes) = div_rem(timestamp, 60);
494        let (timestamp, hours) = div_rem(timestamp, 24);
495        let days = timestamp;
496
497        if days == 0 {
498            match precision {
499                TimePrecision::Micros => write!(
500                    buf,
501                    "{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>6}"
502                ),
503                TimePrecision::Millis => write!(
504                    buf,
505                    "{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>3}"
506                ),
507                TimePrecision::Seconds => write!(buf, "{hours:0>2}:{minutes:0>2}:{seconds:0>2}"),
508            }
509        } else {
510            match precision {
511                TimePrecision::Micros => write!(
512                    buf,
513                    "{days}:{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>6}"
514                ),
515                TimePrecision::Millis => write!(
516                    buf,
517                    "{days}:{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>3}"
518                ),
519                TimePrecision::Seconds => {
520                    write!(buf, "{days}:{hours:0>2}:{minutes:0>2}:{seconds:0>2}")
521                }
522            }
523        }
524    }
525
526    fn format_iso8601(
527        &self,
528        timestamp: u64,
529        precision: &TimePrecision,
530        buf: &mut String,
531    ) -> Result<(), fmt::Error> {
532        let format = match precision {
533            TimePrecision::Micros => format_description!(
534                "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z"
535            ),
536            TimePrecision::Millis => format_description!(
537                "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z"
538            ),
539            TimePrecision::Seconds => {
540                format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z")
541            }
542        };
543        let date_time = OffsetDateTime::from_unix_timestamp_nanos(match precision {
544            TimePrecision::Micros => timestamp as i128 * 1_000,
545            TimePrecision::Millis => timestamp as i128 * 1_000_000,
546            TimePrecision::Seconds => timestamp as i128 * 1_000_000_000,
547        })
548        .unwrap();
549        write!(buf, "{}", date_time.format(format).unwrap())
550    }
551}
552
553pub struct DisplayTimestamp<'t> {
554    frame: &'t Frame<'t>,
555}
556
557impl fmt::Display for DisplayTimestamp<'_> {
558    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
559        let args = self.frame.format_args(
560            self.frame.timestamp_format.unwrap(),
561            &self.frame.timestamp_args,
562            None,
563        );
564        f.write_str(&args)
565    }
566}
567
568pub struct DisplayMessage<'t> {
569    frame: &'t Frame<'t>,
570}
571
572impl fmt::Display for DisplayMessage<'_> {
573    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574        let args = self
575            .frame
576            .format_args(self.frame.format, &self.frame.args, None);
577        f.write_str(&args)
578    }
579}
580
581/// An iterator over the fragments of a log message, formatted as strings.
582///
583/// See [`Frame::display_fragments`].
584pub struct DisplayFragments<'t> {
585    frame: &'t Frame<'t>,
586    iter: std::vec::IntoIter<Fragment<'t>>,
587}
588
589impl Iterator for DisplayFragments<'_> {
590    type Item = String;
591
592    fn next(&mut self) -> Option<Self::Item> {
593        let mut buf = String::new();
594        self.frame
595            .format_fragment(self.iter.next()?, &mut buf, &self.frame.args, None)
596            .ok()?;
597        Some(buf)
598    }
599}
600
601/// Prints a `Frame` when formatted via `fmt::Display`, including all included metadata (level,
602/// timestamp, ...).
603pub struct DisplayFrame<'t> {
604    frame: &'t Frame<'t>,
605    colored: bool,
606}
607
608impl fmt::Display for DisplayFrame<'_> {
609    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610        let level = if let Some(level) = self.frame.level {
611            let level = if self.colored {
612                match level {
613                    Level::Trace => "TRACE".dimmed().to_string(),
614                    Level::Debug => "DEBUG".normal().to_string(),
615                    Level::Info => "INFO".green().to_string(),
616                    Level::Warn => "WARN".yellow().to_string(),
617                    Level::Error => "ERROR".red().to_string(),
618                }
619            } else {
620                match level {
621                    Level::Trace => "TRACE".to_string(),
622                    Level::Debug => "DEBUG".to_string(),
623                    Level::Info => "INFO".to_string(),
624                    Level::Warn => "WARN".to_string(),
625                    Level::Error => "ERROR".to_string(),
626                }
627            };
628            format!("{level} ")
629        } else {
630            "".to_string()
631        };
632
633        let timestamp = self
634            .frame
635            .timestamp_format
636            .map(|fmt| {
637                format!(
638                    "{} ",
639                    self.frame
640                        .format_args(fmt, &self.frame.timestamp_args, None,),
641                )
642            })
643            .unwrap_or_default();
644
645        let args = self
646            .frame
647            .format_args(self.frame.format, &self.frame.args, None);
648
649        write!(f, "{timestamp}{level}{args}")
650    }
651}