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    pub fn level(&self) -> Option<Level> {
95        self.level
96    }
97
98    pub fn index(&self) -> u64 {
99        self.index
100    }
101
102    fn format_args(&self, format: &str, args: &[Arg], parent_hint: Option<&DisplayHint>) -> String {
103        self.format_args_real(format, args, parent_hint).unwrap() // cannot fail, we only write to a `String`
104    }
105
106    fn format_args_real(
107        &self,
108        format: &str,
109        args: &[Arg],
110        parent_hint: Option<&DisplayHint>,
111    ) -> Result<String, fmt::Error> {
112        let params = defmt_parser::parse(format, ParserMode::ForwardsCompatible).unwrap();
113        let mut buf = String::new();
114        for param in params {
115            match param {
116                Fragment::Literal(lit) => {
117                    buf.push_str(&lit);
118                }
119                Fragment::Parameter(param) => {
120                    let hint = param.hint.as_ref().or(parent_hint);
121
122                    match &args[param.index] {
123                        Arg::Bool(x) => write!(buf, "{x}")?,
124                        Arg::F32(x) => write!(buf, "{}", ryu::Buffer::new().format(*x))?,
125                        Arg::F64(x) => write!(buf, "{}", ryu::Buffer::new().format(*x))?,
126                        Arg::Uxx(x) => {
127                            match param.ty {
128                                Type::BitField(range) => {
129                                    let left_zeroes =
130                                        mem::size_of::<u128>() * 8 - range.end as usize;
131                                    let right_zeroes = left_zeroes + range.start as usize;
132                                    // isolate the desired bitfields
133                                    let bitfields = (*x << left_zeroes) >> right_zeroes;
134
135                                    if let Some(DisplayHint::Ascii) = hint {
136                                        let bstr = bitfields
137                                            .to_be_bytes()
138                                            .iter()
139                                            .skip(right_zeroes / 8)
140                                            .copied()
141                                            .collect::<Vec<u8>>();
142                                        self.format_bytes(&bstr, hint, &mut buf)?
143                                    } else {
144                                        self.format_u128(bitfields, hint, &mut buf)?;
145                                    }
146                                }
147                                _ => match hint {
148                                    Some(DisplayHint::ISO8601(precision)) => {
149                                        self.format_iso8601(*x as u64, precision, &mut buf)?
150                                    }
151                                    Some(DisplayHint::Debug) => {
152                                        self.format_u128(*x, parent_hint, &mut buf)?
153                                    }
154                                    _ => self.format_u128(*x, hint, &mut buf)?,
155                                },
156                            }
157                        }
158                        Arg::Ixx(x) => self.format_i128(*x, param.ty, hint, &mut buf)?,
159                        Arg::Str(x) | Arg::Preformatted(x) => self.format_str(x, hint, &mut buf)?,
160                        Arg::IStr(x) => self.format_str(x, hint, &mut buf)?,
161                        Arg::Format { format, args } => match parent_hint {
162                            Some(DisplayHint::Ascii) => {
163                                buf.push_str(&self.format_args(format, args, parent_hint));
164                            }
165                            _ => buf.push_str(&self.format_args(format, args, hint)),
166                        },
167                        Arg::FormatSequence { args } => {
168                            for arg in args {
169                                buf.push_str(&self.format_args("{=?}", &[arg.clone()], hint))
170                            }
171                        }
172                        Arg::FormatSlice { elements } => {
173                            match hint {
174                                // Filter Ascii Hints, which contains u8 byte slices
175                                Some(DisplayHint::Ascii)
176                                    if elements.iter().filter(|e| e.format == "{=u8}").count()
177                                        != 0 =>
178                                {
179                                    let vals = elements
180                                        .iter()
181                                        .map(|e| match e.args.as_slice() {
182                                            [Arg::Uxx(v)] => u8::try_from(*v)
183                                                .expect("the value must be in u8 range"),
184                                            _ => panic!(
185                                                "FormatSlice should only contain one argument"
186                                            ),
187                                        })
188                                        .collect::<Vec<u8>>();
189                                    self.format_bytes(&vals, hint, &mut buf)?
190                                }
191                                _ => {
192                                    buf.write_str("[")?;
193                                    let mut is_first = true;
194                                    for element in elements {
195                                        if !is_first {
196                                            buf.write_str(", ")?;
197                                        }
198                                        is_first = false;
199                                        buf.write_str(&self.format_args(
200                                            element.format,
201                                            &element.args,
202                                            hint,
203                                        ))?;
204                                    }
205                                    buf.write_str("]")?;
206                                }
207                            }
208                        }
209                        Arg::Slice(x) => self.format_bytes(x, hint, &mut buf)?,
210                        Arg::Char(c) => write!(buf, "{c}")?,
211                    }
212                }
213            }
214        }
215        Ok(buf)
216    }
217
218    fn format_u128(
219        &self,
220        x: u128,
221        hint: Option<&DisplayHint>,
222        buf: &mut String,
223    ) -> Result<(), fmt::Error> {
224        match hint {
225            Some(DisplayHint::NoHint { zero_pad }) => write!(buf, "{x:0zero_pad$}")?,
226            Some(DisplayHint::Binary {
227                alternate,
228                zero_pad,
229            }) => match alternate {
230                true => write!(buf, "{x:#0zero_pad$b}")?,
231                false => write!(buf, "{x:0zero_pad$b}")?,
232            },
233            Some(DisplayHint::Octal {
234                alternate,
235                zero_pad,
236            }) => match alternate {
237                true => write!(buf, "{x:#0zero_pad$o}")?,
238                false => write!(buf, "{x:0zero_pad$o}")?,
239            },
240            Some(DisplayHint::Hexadecimal {
241                uppercase,
242                alternate,
243                zero_pad,
244            }) => match (alternate, uppercase) {
245                (false, false) => write!(buf, "{x:0zero_pad$x}")?,
246                (false, true) => write!(buf, "{x:0zero_pad$X}")?,
247                (true, false) => write!(buf, "{x:#0zero_pad$x}")?,
248                (true, true) => write!(buf, "{x:#0zero_pad$X}")?,
249            },
250            Some(DisplayHint::Seconds(TimePrecision::Micros)) => {
251                let seconds = x / 1_000_000;
252                let micros = x % 1_000_000;
253                write!(buf, "{seconds}.{micros:06}")?;
254            }
255            Some(DisplayHint::Seconds(TimePrecision::Millis)) => {
256                let seconds = x / 1_000;
257                let millis = x % 1_000;
258                write!(buf, "{seconds}.{millis:03}")?;
259            }
260            Some(DisplayHint::Time(TimePrecision::Micros)) => {
261                self.format_time(x, &TimePrecision::Micros, buf)?;
262            }
263            Some(DisplayHint::Time(TimePrecision::Millis)) => {
264                self.format_time(x, &TimePrecision::Millis, buf)?;
265            }
266            Some(DisplayHint::Time(TimePrecision::Seconds)) => {
267                self.format_time(x, &TimePrecision::Seconds, buf)?;
268            }
269            Some(DisplayHint::Bitflags {
270                name,
271                package,
272                disambiguator,
273                crate_name,
274            }) => {
275                // The bitflags hint is only used internally, in `Format` impls generated by
276                // `defmt::bitflags!`.
277                let key = BitflagsKey {
278                    ident: name.clone(),
279                    package: package.clone(),
280                    disambig: disambiguator.clone(),
281                    crate_name: crate_name.clone(),
282                };
283                match self.table.bitflags.get(&key) {
284                    Some(flags) => {
285                        let set_flags = flags
286                            .iter()
287                            .filter(|(_, value)| {
288                                if *value == 0 && x != 0 {
289                                    false
290                                } else {
291                                    x & value == *value
292                                }
293                            })
294                            .map(|(name, _)| name.clone())
295                            .collect::<Vec<_>>();
296                        if set_flags.is_empty() {
297                            write!(buf, "(empty)")?;
298                        } else {
299                            write!(buf, "{}", set_flags.join(" | "))?;
300                        }
301                    }
302                    None => {
303                        // FIXME return an internal error here
304                        write!(buf, "{x}")?;
305                    }
306                }
307            }
308            _ => write!(buf, "{x}")?,
309        }
310        Ok(())
311    }
312
313    fn format_i128(
314        &self,
315        x: i128,
316        ty: Type,
317        hint: Option<&DisplayHint>,
318        buf: &mut String,
319    ) -> Result<(), fmt::Error> {
320        match hint {
321            Some(DisplayHint::NoHint { zero_pad }) => write!(buf, "{x:0zero_pad$}")?,
322            Some(DisplayHint::Binary {
323                alternate,
324                zero_pad,
325            }) => match alternate {
326                true => write!(buf, "{x:#0zero_pad$b}")?,
327                false => write!(buf, "{x:0zero_pad$b}")?,
328            },
329            Some(DisplayHint::Octal {
330                alternate,
331                zero_pad,
332            }) => match alternate {
333                true => write!(buf, "{x:#0zero_pad$o}")?,
334                false => write!(buf, "{x:0zero_pad$o}")?,
335            },
336            Some(DisplayHint::Hexadecimal {
337                uppercase,
338                alternate,
339                zero_pad,
340            }) => {
341                let value = I128Hex(x, ty);
342                match (alternate, uppercase) {
343                    (false, false) => write!(buf, "{value:0zero_pad$x}")?,
344                    (false, true) => write!(buf, "{value:0zero_pad$X}")?,
345                    (true, false) => write!(buf, "{value:#0zero_pad$x}")?,
346                    (true, true) => write!(buf, "{value:#0zero_pad$X}")?,
347                }
348            }
349            _ => write!(buf, "{x}")?,
350        }
351        Ok(())
352    }
353
354    fn format_bytes(
355        &self,
356        bytes: &[u8],
357        hint: Option<&DisplayHint>,
358        buf: &mut String,
359    ) -> Result<(), fmt::Error> {
360        match hint {
361            Some(DisplayHint::Ascii) => {
362                // byte string literal syntax: b"Hello\xffworld"
363                buf.push_str("b\"");
364                for byte in bytes {
365                    match byte {
366                        // special escaping
367                        b'\t' => buf.push_str("\\t"),
368                        b'\n' => buf.push_str("\\n"),
369                        b'\r' => buf.push_str("\\r"),
370                        b' ' => buf.push(' '),
371                        b'\"' => buf.push_str("\\\""),
372                        b'\\' => buf.push_str("\\\\"),
373                        _ => {
374                            if byte.is_ascii_graphic() {
375                                buf.push(*byte as char);
376                            } else {
377                                // general escaped form
378                                write!(buf, "\\x{byte:02x}").ok();
379                            }
380                        }
381                    }
382                }
383                buf.push('\"');
384            }
385            Some(DisplayHint::Hexadecimal { .. })
386            | Some(DisplayHint::Octal { .. })
387            | Some(DisplayHint::Binary { .. }) => {
388                // `core::write!` doesn't quite produce the output we want, for example
389                // `write!("{:#04x?}", bytes)` produces a multi-line output
390                // `write!("{:02x?}", bytes)` is single-line but each byte doesn't include the "0x" prefix
391                buf.push('[');
392                let mut is_first = true;
393                for byte in bytes {
394                    if !is_first {
395                        buf.push_str(", ");
396                    }
397                    is_first = false;
398                    self.format_u128(*byte as u128, hint, buf)?;
399                }
400                buf.push(']');
401            }
402            _ => write!(buf, "{bytes:?}")?,
403        }
404        Ok(())
405    }
406
407    fn format_str(
408        &self,
409        s: &str,
410        hint: Option<&DisplayHint>,
411        buf: &mut String,
412    ) -> Result<(), fmt::Error> {
413        if hint == Some(&DisplayHint::Debug) {
414            write!(buf, "{s:?}")?;
415        } else {
416            buf.push_str(s);
417        }
418        Ok(())
419    }
420
421    fn format_time(
422        &self,
423        timestamp: u128,
424        precision: &TimePrecision,
425        buf: &mut String,
426    ) -> Result<(), fmt::Error> {
427        let div_rem = |x, y| (x / y, x % y);
428
429        let (timestamp, decimals) = match precision {
430            TimePrecision::Micros => div_rem(timestamp, 1_000_000),
431            TimePrecision::Millis => div_rem(timestamp, 1_000),
432            TimePrecision::Seconds => (timestamp, 0),
433        };
434
435        let (timestamp, seconds) = div_rem(timestamp, 60);
436        let (timestamp, minutes) = div_rem(timestamp, 60);
437        let (timestamp, hours) = div_rem(timestamp, 24);
438        let days = timestamp;
439
440        if days == 0 {
441            match precision {
442                TimePrecision::Micros => write!(
443                    buf,
444                    "{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>6}"
445                ),
446                TimePrecision::Millis => write!(
447                    buf,
448                    "{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>3}"
449                ),
450                TimePrecision::Seconds => write!(buf, "{hours:0>2}:{minutes:0>2}:{seconds:0>2}"),
451            }
452        } else {
453            match precision {
454                TimePrecision::Micros => write!(
455                    buf,
456                    "{days}:{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>6}"
457                ),
458                TimePrecision::Millis => write!(
459                    buf,
460                    "{days}:{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{decimals:0>3}"
461                ),
462                TimePrecision::Seconds => {
463                    write!(buf, "{days}:{hours:0>2}:{minutes:0>2}:{seconds:0>2}")
464                }
465            }
466        }
467    }
468
469    fn format_iso8601(
470        &self,
471        timestamp: u64,
472        precision: &TimePrecision,
473        buf: &mut String,
474    ) -> Result<(), fmt::Error> {
475        let format = match precision {
476            TimePrecision::Micros => format_description!(
477                "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z"
478            ),
479            TimePrecision::Millis => format_description!(
480                "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z"
481            ),
482            TimePrecision::Seconds => {
483                format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z")
484            }
485        };
486        let date_time = OffsetDateTime::from_unix_timestamp_nanos(match precision {
487            TimePrecision::Micros => timestamp as i128 * 1_000,
488            TimePrecision::Millis => timestamp as i128 * 1_000_000,
489            TimePrecision::Seconds => timestamp as i128 * 1_000_000_000,
490        })
491        .unwrap();
492        write!(buf, "{}", date_time.format(format).unwrap())
493    }
494}
495
496pub struct DisplayTimestamp<'t> {
497    frame: &'t Frame<'t>,
498}
499
500impl fmt::Display for DisplayTimestamp<'_> {
501    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
502        let args = self.frame.format_args(
503            self.frame.timestamp_format.unwrap(),
504            &self.frame.timestamp_args,
505            None,
506        );
507        f.write_str(&args)
508    }
509}
510
511pub struct DisplayMessage<'t> {
512    frame: &'t Frame<'t>,
513}
514
515impl fmt::Display for DisplayMessage<'_> {
516    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517        let args = self
518            .frame
519            .format_args(self.frame.format, &self.frame.args, None);
520        f.write_str(&args)
521    }
522}
523
524/// Prints a `Frame` when formatted via `fmt::Display`, including all included metadata (level,
525/// timestamp, ...).
526pub struct DisplayFrame<'t> {
527    frame: &'t Frame<'t>,
528    colored: bool,
529}
530
531impl fmt::Display for DisplayFrame<'_> {
532    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533        let level = if let Some(level) = self.frame.level {
534            let level = if self.colored {
535                match level {
536                    Level::Trace => "TRACE".dimmed().to_string(),
537                    Level::Debug => "DEBUG".normal().to_string(),
538                    Level::Info => "INFO".green().to_string(),
539                    Level::Warn => "WARN".yellow().to_string(),
540                    Level::Error => "ERROR".red().to_string(),
541                }
542            } else {
543                match level {
544                    Level::Trace => "TRACE".to_string(),
545                    Level::Debug => "DEBUG".to_string(),
546                    Level::Info => "INFO".to_string(),
547                    Level::Warn => "WARN".to_string(),
548                    Level::Error => "ERROR".to_string(),
549                }
550            };
551            format!("{level} ")
552        } else {
553            "".to_string()
554        };
555
556        let timestamp = self
557            .frame
558            .timestamp_format
559            .map(|fmt| {
560                format!(
561                    "{} ",
562                    self.frame
563                        .format_args(fmt, &self.frame.timestamp_args, None,),
564                )
565            })
566            .unwrap_or_default();
567
568        let args = self
569            .frame
570            .format_args(self.frame.format, &self.frame.args, None);
571
572        write!(f, "{timestamp}{level}{args}")
573    }
574}