@@ -91,6 +91,46 @@ 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+ /// ```
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+ /// ```
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+ pub fn fragments ( & ' t self ) -> Vec < Fragment < ' t > > {
131+ defmt_parser:: parse ( self . format , ParserMode :: ForwardsCompatible ) . unwrap ( )
132+ }
133+
94134 pub fn level ( & self ) -> Option < Level > {
95135 self . level
96136 }
@@ -100,119 +140,120 @@ impl<'t> Frame<'t> {
100140 }
101141
102142 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`
143+ let params = defmt_parser:: parse ( format, ParserMode :: ForwardsCompatible ) . unwrap ( ) ;
144+ let mut buf = String :: new ( ) ;
145+ for param in params {
146+ self . format_fragment ( param, & mut buf, args, parent_hint)
147+ . unwrap ( ) ; // cannot fail, we only write to a `String`
148+ }
149+ buf
104150 }
105151
106- fn format_args_real (
152+ fn format_fragment (
107153 & self ,
108- format : & str ,
154+ param : Fragment < ' _ > ,
155+ buf : & mut String ,
109156 args : & [ Arg ] ,
110157 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- }
158+ ) -> Result < ( ) , fmt:: Error > {
159+ match param {
160+ Fragment :: Literal ( lit) => {
161+ buf. push_str ( & lit) ;
162+ }
163+ Fragment :: Parameter ( param) => {
164+ let hint = param. hint . as_ref ( ) . or ( parent_hint) ;
165+
166+ match & args[ param. index ] {
167+ Arg :: Bool ( x) => write ! ( buf, "{x}" ) ?,
168+ Arg :: F32 ( x) => write ! ( buf, "{}" , ryu:: Buffer :: new( ) . format( * x) ) ?,
169+ Arg :: F64 ( x) => write ! ( buf, "{}" , ryu:: Buffer :: new( ) . format( * x) ) ?,
170+ Arg :: Uxx ( x) => {
171+ match param. ty {
172+ Type :: BitField ( range) => {
173+ let left_zeroes = mem:: size_of :: < u128 > ( ) * 8 - range. end as usize ;
174+ let right_zeroes = left_zeroes + range. start as usize ;
175+ // isolate the desired bitfields
176+ let bitfields = ( * x << left_zeroes) >> right_zeroes;
177+
178+ if let Some ( DisplayHint :: Ascii ) = hint {
179+ let bstr = bitfields
180+ . to_be_bytes ( )
181+ . iter ( )
182+ . skip ( right_zeroes / 8 )
183+ . copied ( )
184+ . collect :: < Vec < u8 > > ( ) ;
185+ self . format_bytes ( & bstr, hint, buf) ?
186+ } else {
187+ self . format_u128 ( bitfields, hint, buf) ?;
146188 }
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- } ,
156189 }
190+ _ => match hint {
191+ Some ( DisplayHint :: ISO8601 ( precision) ) => {
192+ self . format_iso8601 ( * x as u64 , precision, buf) ?
193+ }
194+ Some ( DisplayHint :: Debug ) => {
195+ self . format_u128 ( * x, parent_hint, buf) ?
196+ }
197+ _ => self . format_u128 ( * x, hint, buf) ?,
198+ } ,
157199 }
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- }
200+ }
201+ Arg :: Ixx ( x) => self . format_i128 ( * x, param. ty , hint, buf) ?,
202+ Arg :: Str ( x) | Arg :: Preformatted ( x) => self . format_str ( x, hint, buf) ?,
203+ Arg :: IStr ( x) => self . format_str ( x, hint, buf) ?,
204+ Arg :: Format { format, args } => match parent_hint {
205+ Some ( DisplayHint :: Ascii ) => {
206+ buf. push_str ( & self . format_args ( format, args, parent_hint) ) ;
171207 }
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 ( ", " ) ?;
208+ _ => buf. push_str ( & self . format_args ( format, args, hint) ) ,
209+ } ,
210+ Arg :: FormatSequence { args } => {
211+ for arg in args {
212+ buf. push_str ( & self . format_args ( "{=?}" , & [ arg. clone ( ) ] , hint) )
213+ }
214+ }
215+ Arg :: FormatSlice { elements } => {
216+ match hint {
217+ // Filter Ascii Hints, which contains u8 byte slices
218+ Some ( DisplayHint :: Ascii )
219+ if elements. iter ( ) . filter ( |e| e. format == "{=u8}" ) . count ( ) != 0 =>
220+ {
221+ let vals = elements
222+ . iter ( )
223+ . map ( |e| match e. args . as_slice ( ) {
224+ [ Arg :: Uxx ( v) ] => {
225+ u8:: try_from ( * v) . expect ( "the value must be in u8 range" )
197226 }
198- is_first = false ;
199- buf. write_str ( & self . format_args (
200- element. format ,
201- & element. args ,
202- hint,
203- ) ) ?;
227+ _ => panic ! ( "FormatSlice should only contain one argument" ) ,
228+ } )
229+ . collect :: < Vec < u8 > > ( ) ;
230+ self . format_bytes ( & vals, hint, buf) ?
231+ }
232+ _ => {
233+ buf. write_str ( "[" ) ?;
234+ let mut is_first = true ;
235+ for element in elements {
236+ if !is_first {
237+ buf. write_str ( ", " ) ?;
204238 }
205- buf. write_str ( "]" ) ?;
239+ is_first = false ;
240+ buf. write_str ( & self . format_args (
241+ element. format ,
242+ & element. args ,
243+ hint,
244+ ) ) ?;
206245 }
246+ buf. write_str ( "]" ) ?;
207247 }
208248 }
209- Arg :: Slice ( x) => self . format_bytes ( x, hint, & mut buf) ?,
210- Arg :: Char ( c) => write ! ( buf, "{c}" ) ?,
211249 }
250+ Arg :: Slice ( x) => self . format_bytes ( x, hint, buf) ?,
251+ Arg :: Char ( c) => write ! ( buf, "{c}" ) ?,
212252 }
213253 }
214254 }
215- Ok ( buf)
255+
256+ Ok ( ( ) )
216257 }
217258
218259 fn format_u128 (
@@ -531,6 +572,23 @@ impl fmt::Display for DisplayMessage<'_> {
531572 }
532573}
533574
575+ pub struct DisplayFragments < ' t > {
576+ frame : & ' t Frame < ' t > ,
577+ iter : std:: vec:: IntoIter < Fragment < ' t > > ,
578+ }
579+
580+ impl Iterator for DisplayFragments < ' _ > {
581+ type Item = String ;
582+
583+ fn next ( & mut self ) -> Option < Self :: Item > {
584+ let mut buf = String :: new ( ) ;
585+ self . frame
586+ . format_fragment ( self . iter . next ( ) ?, & mut buf, & self . frame . args , None )
587+ . ok ( ) ?;
588+ Some ( buf)
589+ }
590+ }
591+
534592/// Prints a `Frame` when formatted via `fmt::Display`, including all included metadata (level,
535593/// timestamp, ...).
536594pub struct DisplayFrame < ' t > {
0 commit comments