@@ -91,6 +91,52 @@ impl<'t> Frame<'t> {
9191 DisplayMessage { frame : self }
9292 }
9393
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+
94140 pub fn level ( & self ) -> Option < Level > {
95141 self . level
96142 }
@@ -100,119 +146,120 @@ impl<'t> Frame<'t> {
100146 }
101147
102148 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`
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
104156 }
105157
106- fn format_args_real (
158+ fn format_fragment (
107159 & self ,
108- format : & str ,
160+ param : Fragment < ' _ > ,
161+ buf : & mut String ,
109162 args : & [ Arg ] ,
110163 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- }
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) ?;
146194 }
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- } ,
156195 }
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+ } ,
157205 }
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- }
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) ) ;
171213 }
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 ( ", " ) ?;
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" )
197232 }
198- is_first = false ;
199- buf. write_str ( & self . format_args (
200- element. format ,
201- & element. args ,
202- hint,
203- ) ) ?;
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 ( ", " ) ?;
204244 }
205- buf. write_str ( "]" ) ?;
245+ is_first = false ;
246+ buf. write_str ( & self . format_args (
247+ element. format ,
248+ & element. args ,
249+ hint,
250+ ) ) ?;
206251 }
252+ buf. write_str ( "]" ) ?;
207253 }
208254 }
209- Arg :: Slice ( x) => self . format_bytes ( x, hint, & mut buf) ?,
210- Arg :: Char ( c) => write ! ( buf, "{c}" ) ?,
211255 }
256+ Arg :: Slice ( x) => self . format_bytes ( x, hint, buf) ?,
257+ Arg :: Char ( c) => write ! ( buf, "{c}" ) ?,
212258 }
213259 }
214260 }
215- Ok ( buf)
261+
262+ Ok ( ( ) )
216263 }
217264
218265 fn format_u128 (
@@ -531,6 +578,26 @@ impl fmt::Display for DisplayMessage<'_> {
531578 }
532579}
533580
581+ /// An iterator over the fragments of a log message, formatted as strings.
582+ ///
583+ /// See [`Frame::display_fragments`].
584+ pub struct DisplayFragments < ' t > {
585+ frame : & ' t Frame < ' t > ,
586+ iter : std:: vec:: IntoIter < Fragment < ' t > > ,
587+ }
588+
589+ impl 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+
534601/// Prints a `Frame` when formatted via `fmt::Display`, including all included metadata (level,
535602/// timestamp, ...).
536603pub struct DisplayFrame < ' t > {
0 commit comments