pw_format/
macros.rs

1// Copyright 2023 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15//! The `macro` module provides helpers that simplify writing proc macros
16//! that take format strings and arguments.  This is accomplish with three
17//! main constructs:
18//! * [`FormatAndArgsFlavor`]: A struct that implements [syn::parse::Parse] to
19//!   parse a format string and its following arguments.
20//! * [`FormatMacroGenerator`]: A trait used to implement the macro specific
21//!   logic to generate code.
22//! * [`generate`]: A function to handle the execution of the proc macro by
23//!   calling into a [FormatMacroGenerator].
24//!
25//! Additionally [`PrintfFormatMacroGenerator`] trait and [`generate_printf`]
26//! function are provided to help when implementing generators that need to
27//! produce `printf` style format strings as part of their code generation.
28//!
29//! ## Example
30//!
31//! An example of implementing a proc macro is provided in the
32//! [pw_format_example_macro crate](https://pigweed.googlesource.com/pigweed/pigweed/+/refs/heads/main/pw_format/rust/pw_format_example_macro.rs)
33//!
34//!
35
36use std::collections::VecDeque;
37use std::marker::PhantomData;
38
39use proc_macro2::Ident;
40use quote::{format_ident, quote, ToTokens};
41use syn::{
42    parse::{Parse, ParseStream},
43    punctuated::Punctuated,
44    spanned::Spanned,
45    Expr, ExprCast, LitStr, Token,
46};
47
48use crate::{
49    ConversionSpec, Flag, FormatFragment, FormatString, Length, MinFieldWidth, Precision,
50    Primitive, Style,
51};
52
53mod keywords {
54    syn::custom_keyword!(PW_FMT_CONCAT);
55}
56
57type TokenStream2 = proc_macro2::TokenStream;
58
59/// An error occurring during proc macro evaluation.
60///
61/// In order to stay as flexible as possible to implementors of
62/// [`FormatMacroGenerator`], the error is simply represent by a
63/// string.
64#[derive(Debug)]
65pub struct Error {
66    text: String,
67}
68
69impl Error {
70    /// Create a new proc macro evaluation error.
71    pub fn new(text: &str) -> Self {
72        Self {
73            text: text.to_string(),
74        }
75    }
76}
77
78/// An alias for a Result with an ``Error``
79pub type Result<T> = core::result::Result<T, Error>;
80
81/// Formatting parameters passed to an untyped conversion.
82#[derive(Clone, Debug, PartialEq)]
83pub struct FormatParams {
84    /// Style in which to print the corresponding argument.
85    pub style: Style,
86
87    /// Minimum field width.  (i.e. the `8` in `{:08x}`).
88    pub min_field_width: Option<u32>,
89
90    /// Zero padding.  (i.e. the existence of `0` in `{:08x}`).
91    pub zero_padding: bool,
92
93    /// Alternate syntax.  (i.e. the existence of `#` in `{:#08x}`).
94    pub alternate_syntax: bool,
95}
96
97impl FormatParams {
98    fn printf_format_trait(&self) -> Result<Ident> {
99        match self.style {
100            Style::None => Ok(format_ident!("PrintfFormatter")),
101            Style::Hex => Ok(format_ident!("PrintfHexFormatter")),
102            Style::UpperHex => Ok(format_ident!("PrintfUpperHexFormatter")),
103            _ => Err(Error::new(&format!(
104                "formatting untyped conversions with {:?} style is unsupported",
105                self.style
106            ))),
107        }
108    }
109
110    fn field_params(&self) -> String {
111        let (zero_pad, min_field_width) = match self.min_field_width {
112            None => ("", "".to_string()),
113            Some(min_field_width) => (
114                if self.zero_padding { "0" } else { "" },
115                format!("{min_field_width}"),
116            ),
117        };
118
119        let alternate_syntax = if self.alternate_syntax { "#" } else { "" };
120
121        format!("{alternate_syntax}{zero_pad}{min_field_width}")
122    }
123
124    fn core_fmt_specifier(&self) -> Result<String> {
125        // If no formatting options are needed, omit the `:`.
126        if self.style == Style::None && self.min_field_width.is_none() {
127            return Ok("{}".to_string());
128        }
129
130        let format = match self.style {
131            Style::None => "",
132            Style::Octal => "o",
133            Style::Hex => "x",
134            Style::UpperHex => "X",
135            _ => {
136                return Err(Error::new(&format!(
137                    "formatting untyped conversions with {:?} style is unsupported",
138                    self.style
139                )))
140            }
141        };
142
143        let field_params = self.field_params();
144
145        Ok(format!("{{:{field_params}{format}}}"))
146    }
147}
148
149impl TryFrom<&ConversionSpec> for FormatParams {
150    type Error = Error;
151    fn try_from(spec: &ConversionSpec) -> core::result::Result<Self, Self::Error> {
152        let min_field_width = match spec.min_field_width {
153            MinFieldWidth::None => None,
154            MinFieldWidth::Fixed(len) => Some(len),
155            MinFieldWidth::Variable => {
156                return Err(Error::new(
157                    "Variable width '*' string formats are not supported.",
158                ))
159            }
160        };
161
162        Ok(FormatParams {
163            style: spec.style,
164            min_field_width,
165            zero_padding: spec.flags.contains(&Flag::LeadingZeros),
166            alternate_syntax: spec.flags.contains(&Flag::AlternateSyntax),
167        })
168    }
169}
170
171/// Implemented for testing through the pw_format_test_macros crate.
172impl ToTokens for FormatParams {
173    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
174        let style = self.style;
175        let min_field_width = match self.min_field_width {
176            None => quote! {None},
177            Some(val) => quote! {Some(#val)},
178        };
179        let zero_padding = self.zero_padding;
180        let alternate_syntax = self.alternate_syntax;
181
182        quote! {
183            pw_format::macros::FormatParams {
184                style: #style,
185                min_field_width: #min_field_width,
186                zero_padding: #zero_padding,
187                alternate_syntax: #alternate_syntax,
188            }
189        }
190        .to_tokens(tokens)
191    }
192}
193
194/// A code generator for implementing a `pw_format` style macro.
195///
196/// This trait serves as the primary interface between `pw_format` and a
197/// proc macro using it to implement format string and argument parsing.  When
198/// evaluating the proc macro and generating code, [`generate`] will make
199/// repeated calls to [`string_fragment`](FormatMacroGenerator::string_fragment)
200/// and the conversion functions.  These calls will be made in the order they
201/// appear in the format string.  After all fragments and conversions are
202/// processed, [`generate`] will call
203/// [`finalize`](FormatMacroGenerator::finalize).
204///
205/// For an example of implementing a `FormatMacroGenerator` see the
206/// [pw_format_example_macro crate](https://pigweed.googlesource.com/pigweed/pigweed/+/refs/heads/main/pw_format/rust/pw_format_example_macro.rs).
207pub trait FormatMacroGenerator {
208    /// Called by [`generate`] at the end of code generation.
209    ///
210    /// Consumes `self` and returns the code to be emitted by the proc macro of
211    /// and [`Error`].
212    fn finalize(self) -> Result<TokenStream2>;
213
214    /// Process a string fragment.
215    ///
216    /// A string fragment is a string of characters that appear in a format
217    /// string.  This is different than a
218    /// [`string_conversion`](FormatMacroGenerator::string_conversion) which is
219    /// a string provided through a conversion specifier (i.e. `"%s"`).
220    fn string_fragment(&mut self, string: &str) -> Result<()>;
221
222    /// Process an integer conversion.
223    fn integer_conversion(
224        &mut self,
225        params: &FormatParams,
226        signed: bool,
227        type_width: u8, // This should probably be an enum
228        expression: Arg,
229    ) -> Result<()>;
230
231    /// Process a string conversion.
232    ///
233    /// See [`string_fragment`](FormatMacroGenerator::string_fragment) for a
234    /// disambiguation between that function and this one.
235    fn string_conversion(&mut self, expression: Arg) -> Result<()>;
236
237    /// Process a character conversion.
238    fn char_conversion(&mut self, expression: Arg) -> Result<()>;
239
240    /// Process an untyped conversion.
241    fn untyped_conversion(&mut self, _expression: Arg, _params: &FormatParams) -> Result<()> {
242        Err(Error::new("untyped conversion (%v) not supported"))
243    }
244}
245
246/// An argument to a `pw_format` backed macro.
247///
248/// `pw_format` backed macros have special case recognition of type casts
249/// (`value as ty`) in order to annotate a type for typeless printing w/o
250/// relying on experimental features.  If an argument is given in that form,
251/// it will be represented as an [`Arg::ExprCast`] here.  Otherwise it will
252/// be an [`Arg::Expr`].
253#[derive(Clone, Debug)]
254pub enum Arg {
255    /// An argument that is an type cast expression.
256    ExprCast(ExprCast),
257    /// An argument that is an expression.
258    Expr(Expr),
259}
260
261impl Arg {
262    fn parse_expr(expr: Expr) -> syn::parse::Result<Self> {
263        match expr.clone() {
264            Expr::Cast(cast) => Ok(Self::ExprCast(cast)),
265
266            // Expr::Casts maybe be wrapped in an Expr::Group or in unexplained
267            // cases where macro expansion in the rust-analyzer VSCode plugin
268            // may cause them to be wrapped in an Expr::Paren instead.
269            Expr::Paren(paren) => Self::parse_expr(*paren.expr),
270            Expr::Group(group) => Self::parse_expr(*group.expr),
271
272            _ => Ok(Self::Expr(expr)),
273        }
274    }
275}
276
277impl Parse for Arg {
278    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
279        Self::parse_expr(input.parse::<Expr>()?)
280    }
281}
282
283impl ToTokens for Arg {
284    fn to_tokens(&self, tokens: &mut TokenStream2) {
285        match self {
286            Self::Expr(expr) => expr.to_tokens(tokens),
287            Self::ExprCast(cast) => cast.to_tokens(tokens),
288        }
289    }
290}
291
292/// A trait for parsing a string into a [`FormatString`].
293pub trait FormatStringParser {
294    /// Parse `format_string` and return the results as a `[FormatString]`.
295    fn parse_format_string(format_string: &str) -> std::result::Result<FormatString, String>;
296}
297
298/// An implementation of [`FormatStringParser`] that parsers `printf` style format strings.
299#[derive(Debug)]
300pub struct PrintfFormatStringParser;
301impl FormatStringParser for PrintfFormatStringParser {
302    fn parse_format_string(format_string: &str) -> std::result::Result<FormatString, String> {
303        FormatString::parse_printf(format_string)
304    }
305}
306
307/// An implementation of [`FormatStringParser`] that parsers `core::fmt` style format strings.
308#[derive(Debug)]
309pub struct CoreFmtFormatStringParser;
310impl FormatStringParser for CoreFmtFormatStringParser {
311    fn parse_format_string(format_string: &str) -> std::result::Result<FormatString, String> {
312        FormatString::parse_core_fmt(format_string)
313    }
314}
315
316/// A parsed format string and it's arguments.
317///
318/// To parse a `FormatAndArgs`, use the [`FormatAndArgsFlavor`] variant which
319/// is generic over [`FormatStringParser`] to allow parsing either `printf` or
320/// `core::fmt` style format strings.
321///
322/// Arguments are parsed according to the pattern:
323/// `($format_string:literal, $($args:expr),*)`
324///
325/// To support uses where format strings need to be built by macros at compile
326/// time, the format string can be specified as a set of string literals
327/// separated by the custom `PW_FMT_CONCAT` keyword.
328#[derive(Debug)]
329pub struct FormatAndArgs {
330    format_string: LitStr,
331    parsed: FormatString,
332    args: VecDeque<Arg>,
333}
334
335/// A variant of [`FormatAndArgs`] that is generic over format string flavor.
336///
337/// `FormatAndArgsFlavor` implements [`syn::parse::Parse`] for it's specified
338/// format string flavor.  Instantiate `FormatAndArgsFlavor` with either
339/// [`PrintfFormatStringParser`] or [`CoreFmtFormatStringParser`] to specify
340/// which format string flavor should be used.
341///
342/// `FormatAndArgsFlavor` trivially converts into [`FormatAndArgs`] with the
343/// [`From`] trait.
344#[derive(Debug)]
345pub struct FormatAndArgsFlavor<T: FormatStringParser> {
346    format_and_args: FormatAndArgs,
347    phantom: PhantomData<T>,
348}
349
350impl<T: FormatStringParser> From<FormatAndArgsFlavor<T>> for FormatAndArgs {
351    fn from(val: FormatAndArgsFlavor<T>) -> Self {
352        val.format_and_args
353    }
354}
355
356impl<T: FormatStringParser> Parse for FormatAndArgsFlavor<T> {
357    fn parse(input: ParseStream) -> syn::parse::Result<Self> {
358        let punctuated =
359            Punctuated::<LitStr, keywords::PW_FMT_CONCAT>::parse_separated_nonempty(input)?;
360        let span = punctuated.span();
361        let format_string = LitStr::new(
362            &punctuated.into_iter().fold(String::new(), |mut acc, s| {
363                acc.push_str(&s.value());
364                acc
365            }),
366            span,
367        );
368
369        let args = if input.is_empty() {
370            // If there are no more tokens, no arguments were specified.
371            VecDeque::new()
372        } else {
373            // Eat the `,` following the format string.
374            input.parse::<Token![,]>()?;
375
376            let punctuated = Punctuated::<Arg, Token![,]>::parse_terminated(input)?;
377            punctuated.into_iter().collect()
378        };
379
380        let parsed = T::parse_format_string(&format_string.value()).map_err(|e| {
381            syn::Error::new_spanned(
382                format_string.to_token_stream(),
383                format!("Error parsing format string {e}"),
384            )
385        })?;
386
387        Ok(FormatAndArgsFlavor {
388            format_and_args: FormatAndArgs {
389                format_string,
390                parsed,
391                args,
392            },
393            phantom: PhantomData,
394        })
395    }
396}
397
398// Grab the next argument returning a descriptive error if no more args are left.
399fn next_arg(spec: &ConversionSpec, args: &mut VecDeque<Arg>) -> Result<Arg> {
400    args.pop_front()
401        .ok_or_else(|| Error::new(&format!("No argument given for {spec:?}")))
402}
403
404// Handle a single format conversion specifier (i.e. `%08x`).  Grabs the
405// necessary arguments for the specifier from `args` and generates code
406// to marshal the arguments into the buffer declared in `_tokenize_to_buffer`.
407// Returns an error if args is too short of if a format specifier is unsupported.
408fn handle_conversion(
409    generator: &mut dyn FormatMacroGenerator,
410    spec: &ConversionSpec,
411    args: &mut VecDeque<Arg>,
412) -> Result<()> {
413    match spec.primitive {
414        Primitive::Integer | Primitive::Unsigned => {
415            // TODO: b/281862660 - Support Width::Variable and Precision::Variable.
416            if spec.min_field_width == MinFieldWidth::Variable {
417                return Err(Error::new(
418                    "Variable width '*' integer formats are not supported.",
419                ));
420            }
421
422            if spec.precision == Precision::Variable {
423                return Err(Error::new(
424                    "Variable precision '*' integer formats are not supported.",
425                ));
426            }
427
428            if spec.style == Style::Binary {
429                return Err(Error::new("Binary output style is not supported."));
430            }
431
432            let arg = next_arg(spec, args)?;
433            let bits = match spec.length.unwrap_or(Length::Long) {
434                Length::Char => 8,
435                Length::Short => 16,
436                Length::Long => 32,
437                Length::LongLong => 64,
438                Length::IntMax => 64,
439                Length::Size => 32,
440                Length::PointerDiff => 32,
441                Length::LongDouble => {
442                    return Err(Error::new(
443                        "Long double length parameter invalid for integer formats",
444                    ))
445                }
446            };
447            let params = spec.try_into()?;
448
449            generator.integer_conversion(&params, spec.primitive == Primitive::Integer, bits, arg)
450        }
451        Primitive::String => {
452            // TODO: b/281862660 - Support Width::Variable and Precision::Variable.
453            if spec.min_field_width == MinFieldWidth::Variable {
454                return Err(Error::new(
455                    "Variable width '*' string formats are not supported.",
456                ));
457            }
458
459            if spec.precision == Precision::Variable {
460                return Err(Error::new(
461                    "Variable precision '*' string formats are not supported.",
462                ));
463            }
464
465            let arg = next_arg(spec, args)?;
466            generator.string_conversion(arg)
467        }
468        Primitive::Character => {
469            let arg = next_arg(spec, args)?;
470            generator.char_conversion(arg)
471        }
472
473        Primitive::Untyped => {
474            let arg = next_arg(spec, args)?;
475            let params = spec.try_into()?;
476            generator.untyped_conversion(arg, &params)
477        }
478
479        Primitive::Float => {
480            // TODO: b/281862328 - Support floating point numbers.
481            Err(Error::new("Floating point numbers are not supported."))
482        }
483
484        // TODO: b/281862333 - Support pointers.
485        Primitive::Pointer => Err(Error::new("Pointer types are not supported.")),
486    }
487}
488
489/// Generate code for a `pw_format` style proc macro.
490///
491/// `generate` takes a [`FormatMacroGenerator`] and a [`FormatAndArgs`] struct
492/// and uses them to produce the code output for a proc macro.
493pub fn generate(
494    mut generator: impl FormatMacroGenerator,
495    format_and_args: FormatAndArgs,
496) -> core::result::Result<TokenStream2, syn::Error> {
497    let mut args = format_and_args.args;
498    let mut errors = Vec::new();
499
500    for fragment in format_and_args.parsed.fragments {
501        let result = match fragment {
502            FormatFragment::Conversion(spec) => handle_conversion(&mut generator, &spec, &mut args),
503            FormatFragment::Literal(string) => generator.string_fragment(&string),
504        };
505        if let Err(e) = result {
506            errors.push(syn::Error::new_spanned(
507                format_and_args.format_string.to_token_stream(),
508                e.text,
509            ));
510        }
511    }
512
513    if !errors.is_empty() {
514        return Err(errors
515            .into_iter()
516            .reduce(|mut accumulated_errors, error| {
517                accumulated_errors.combine(error);
518                accumulated_errors
519            })
520            .expect("errors should not be empty"));
521    }
522
523    generator.finalize().map_err(|e| {
524        syn::Error::new_spanned(format_and_args.format_string.to_token_stream(), e.text)
525    })
526}
527
528/// A specialized generator for proc macros that produce `printf` style format strings.
529///
530/// For proc macros that need to translate a `pw_format` invocation into a
531/// `printf` style format string, `PrintfFormatMacroGenerator` offer a
532/// specialized form of [`FormatMacroGenerator`] that builds the format string
533/// and provides it as an argument to
534/// [`finalize`](PrintfFormatMacroGenerator::finalize).
535///
536/// In cases where a generator needs to override the conversion specifier it
537/// can return it from its appropriate conversion method.  An example of using
538/// this would be wanting to pass a Rust string directly to a `printf` call
539/// over FFI.  In that case,
540/// [`string_conversion`](PrintfFormatMacroGenerator::string_conversion) could
541/// return `Ok(Some("%.*s".to_string()))` to allow both the length and string
542/// pointer to be passed to `printf`.
543pub trait PrintfFormatMacroGenerator {
544    /// Called by [`generate_printf`] at the end of code generation.
545    ///
546    /// Works like [`FormatMacroGenerator::finalize`] with the addition of
547    /// being provided a `printf_style` format string.
548    fn finalize(
549        self,
550        format_string_fragments: &[PrintfFormatStringFragment],
551    ) -> Result<TokenStream2>;
552
553    /// Process a string fragment.
554    ///
555    /// **NOTE**: This string may contain unescaped `%` characters.
556    /// However, most implementations of this train can simply ignore string
557    /// fragments as they will be included (with properly escaped `%`
558    /// characters) as part of the format string passed to
559    /// [`PrintfFormatMacroGenerator::finalize`].
560    ///
561    /// See [`FormatMacroGenerator::string_fragment`] for a disambiguation
562    /// between a string fragment and string conversion.
563    fn string_fragment(&mut self, string: &str) -> Result<()>;
564
565    /// Process an integer conversion.
566    ///
567    /// May optionally return a printf format string (i.e. "%d") to override the
568    /// default.
569    fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>;
570
571    /// Process a string conversion.
572    ///
573    /// May optionally return a printf format string (i.e. "%s") to override the
574    /// default.
575    ///
576    /// See [`FormatMacroGenerator::string_fragment`] for a disambiguation
577    /// between a string fragment and string conversion.
578    fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
579
580    /// Process a character conversion.
581    ///
582    /// May optionally return a printf format string (i.e. "%c") to override the
583    /// default.
584    fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
585
586    /// Process and untyped conversion.
587    fn untyped_conversion(&mut self, _expression: Arg) -> Result<()> {
588        Err(Error::new("untyped conversion not supported"))
589    }
590}
591
592/// A fragment of a printf format string.
593///
594/// Printf format strings are built of multiple fragments.  These fragments can
595/// be either a string ([`PrintfFormatStringFragment::String`]) or an expression
596/// that evaluates to a `const &str` ([`PrintfFormatStringFragment::Expr`]).
597/// These fragments can then be used to create a single `const &str` for use by
598/// code generation.
599///
600/// # Example
601/// ```
602/// use pw_bytes::concat_static_strs;
603/// use pw_format::macros::{PrintfFormatStringFragment, Result};
604/// use quote::quote;
605///
606/// fn handle_fragments(format_string_fragments: &[PrintfFormatStringFragment]) -> Result<()> {
607///   let format_string_pieces: Vec<_> = format_string_fragments
608///     .iter()
609///     .map(|fragment| fragment.as_token_stream("__pw_log_backend_crate"))
610///     .collect::<Result<Vec<_>>>()?;
611///
612///   quote! {
613///     let format_string = concat_static_strs!("prefix: ", #(#format_string_pieces),*, "\n");
614///   };
615///   Ok(())
616/// }
617/// ```
618pub enum PrintfFormatStringFragment {
619    /// A fragment that is a string.
620    String(String),
621
622    /// An expressions that can be converted to a `const &str`.
623    Expr {
624        /// Argument to convert.
625        arg: Arg,
626        /// Trait to used for getting the format specifier for the argument.
627        ///
628        /// One of `PrintfFormatter`, `PrintfHexFormatter`, `PrintfUpperHexFormatter
629        format_trait: Ident,
630    },
631}
632
633impl PrintfFormatStringFragment {
634    /// Returns a [`proc_macro2::TokenStream`] representing this fragment.
635    pub fn as_token_stream(&self, printf_formatter_trait_location: &str) -> Result<TokenStream2> {
636        let crate_name = format_ident!("{}", printf_formatter_trait_location);
637        match self {
638            Self::String(s) => Ok(quote! {#s}),
639            #[cfg(not(feature = "nightly_tait"))]
640            Self::Expr { arg, format_trait } => {
641                let Arg::ExprCast(cast) = arg else {
642                    return Err(Error::new(&format!(
643                      "Expected argument to untyped format (%v/{{}}) to be a cast expression (e.g. x as i32), but found {}.",
644                      arg.to_token_stream(), 
645                    )));
646                };
647                let ty = &cast.ty;
648                Ok(quote! {
649                  {
650                    use #crate_name::#format_trait;
651                    <#ty as #format_trait>::FORMAT_ARG
652                  }
653                })
654            }
655            #[cfg(feature = "nightly_tait")]
656            Self::Expr { arg, format_trait } => Ok(quote! {
657              {
658                use #crate_name::#format_trait;
659                type T = impl #format_trait;
660                let _: &T = &(#arg);
661                let arg = <T as #format_trait>::FORMAT_ARG;
662                arg
663              }
664            }),
665        }
666    }
667}
668
669// Wraps a `PrintfFormatMacroGenerator` in a `FormatMacroGenerator` that
670// generates the format string as it goes.
671struct PrintfGenerator<GENERATOR: PrintfFormatMacroGenerator> {
672    inner: GENERATOR,
673    format_string_fragments: Vec<PrintfFormatStringFragment>,
674}
675
676impl<GENERATOR: PrintfFormatMacroGenerator> PrintfGenerator<GENERATOR> {
677    // Append `format_string` to the current set of format string fragments.
678    fn append_format_string(&mut self, format_string: &str) {
679        // If the last fragment is a string, append to that.
680        if let PrintfFormatStringFragment::String(s) = self
681            .format_string_fragments
682            .last_mut()
683            .expect("format_string_fragments always has at least one entry")
684        {
685            s.push_str(format_string)
686        } else {
687            // If the last fragment is not a string, add a new string fragment.
688            self.format_string_fragments
689                .push(PrintfFormatStringFragment::String(
690                    format_string.to_string(),
691                ));
692        }
693    }
694}
695
696impl<GENERATOR: PrintfFormatMacroGenerator> FormatMacroGenerator for PrintfGenerator<GENERATOR> {
697    fn finalize(self) -> Result<TokenStream2> {
698        self.inner.finalize(&self.format_string_fragments)
699    }
700
701    fn string_fragment(&mut self, string: &str) -> Result<()> {
702        // Escape '%' characters.
703        let format_string = string.replace('%', "%%");
704
705        self.append_format_string(&format_string);
706        self.inner.string_fragment(string)
707    }
708
709    fn integer_conversion(
710        &mut self,
711        params: &FormatParams,
712        signed: bool,
713        type_width: u8, // in bits
714        expression: Arg,
715    ) -> Result<()> {
716        let length_modifier = match type_width {
717            8 => "hh",
718            16 => "h",
719            32 => "",
720            64 => "ll",
721            _ => {
722                return Err(Error::new(&format!(
723                    "printf backend does not support {} bit field width",
724                    type_width
725                )))
726            }
727        };
728
729        let (conversion, ty) = match params.style {
730            Style::None => {
731                if signed {
732                    ("d", format_ident!("i{type_width}"))
733                } else {
734                    ("u", format_ident!("u{type_width}"))
735                }
736            }
737            Style::Octal => ("o", format_ident!("u{type_width}")),
738            Style::Hex => ("x", format_ident!("u{type_width}")),
739            Style::UpperHex => ("X", format_ident!("u{type_width}")),
740            _ => {
741                return Err(Error::new(&format!(
742                    "printf backend does not support formatting integers with {:?} style",
743                    params.style
744                )))
745            }
746        };
747
748        match self.inner.integer_conversion(ty, expression)? {
749            Some(s) => self.append_format_string(&s),
750            None => self.append_format_string(&format!(
751                "%{}{}{}",
752                params.field_params(),
753                length_modifier,
754                conversion
755            )),
756        }
757
758        Ok(())
759    }
760
761    fn string_conversion(&mut self, expression: Arg) -> Result<()> {
762        match self.inner.string_conversion(expression)? {
763            Some(s) => self.append_format_string(&s),
764            None => self.append_format_string("%s"),
765        }
766        Ok(())
767    }
768
769    fn char_conversion(&mut self, expression: Arg) -> Result<()> {
770        match self.inner.char_conversion(expression)? {
771            Some(s) => self.append_format_string(&s),
772            None => self.append_format_string("%c"),
773        }
774        Ok(())
775    }
776
777    fn untyped_conversion(&mut self, expression: Arg, params: &FormatParams) -> Result<()> {
778        self.inner.untyped_conversion(expression.clone())?;
779
780        self.append_format_string(&format!("%{}", params.field_params()));
781        self.format_string_fragments
782            .push(PrintfFormatStringFragment::Expr {
783                arg: expression,
784                format_trait: params.printf_format_trait()?,
785            });
786        Ok(())
787    }
788}
789
790/// Generate code for a `pw_format` style proc macro that needs a `printf` format string.
791///
792/// `generate_printf` is a specialized version of [`generate`] which works with
793/// [`PrintfFormatMacroGenerator`]
794pub fn generate_printf(
795    generator: impl PrintfFormatMacroGenerator,
796    format_and_args: FormatAndArgs,
797) -> core::result::Result<TokenStream2, syn::Error> {
798    let generator = PrintfGenerator {
799        inner: generator,
800        format_string_fragments: vec![PrintfFormatStringFragment::String("".into())],
801    };
802    generate(generator, format_and_args)
803}
804
805/// A specialized generator for proc macros that produce [`core::fmt`] style format strings.
806///
807/// For proc macros that need to translate a `pw_format` invocation into a
808/// [`core::fmt`] style format string, `CoreFmtFormatMacroGenerator` offer a
809/// specialized form of [`FormatMacroGenerator`] that builds the format string
810/// and provides it as an argument to
811/// [`finalize`](CoreFmtFormatMacroGenerator::finalize).
812///
813/// In cases where a generator needs to override the conversion specifier (i.e.
814/// `{}`, it can return it from its appropriate conversion method.
815pub trait CoreFmtFormatMacroGenerator {
816    /// Called by [`generate_core_fmt`] at the end of code generation.
817    ///
818    /// Works like [`FormatMacroGenerator::finalize`] with the addition of
819    /// being provided a [`core::fmt`] format string.
820    fn finalize(self, format_string: String) -> Result<TokenStream2>;
821
822    /// Process a string fragment.
823    ///
824    /// **NOTE**: This string may contain unescaped `{` and `}` characters.
825    /// However, most implementations of this train can simply ignore string
826    /// fragments as they will be included (with properly escaped `{` and `}`
827    /// characters) as part of the format string passed to
828    /// [`CoreFmtFormatMacroGenerator::finalize`].
829    ///
830    ///
831    /// See [`FormatMacroGenerator::string_fragment`] for a disambiguation
832    /// between a string fragment and string conversion.
833    fn string_fragment(&mut self, string: &str) -> Result<()>;
834
835    /// Process an integer conversion.
836    fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>;
837
838    /// Process a string conversion.
839    fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
840
841    /// Process a character conversion.
842    fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
843
844    /// Process an untyped conversion.
845    fn untyped_conversion(&mut self, _expression: Arg) -> Result<()> {
846        Err(Error::new("untyped conversion ({}) not supported"))
847    }
848}
849
850// Wraps a `CoreFmtFormatMacroGenerator` in a `FormatMacroGenerator` that
851// generates the format string as it goes.
852struct CoreFmtGenerator<GENERATOR: CoreFmtFormatMacroGenerator> {
853    inner: GENERATOR,
854    format_string: String,
855}
856
857impl<GENERATOR: CoreFmtFormatMacroGenerator> FormatMacroGenerator for CoreFmtGenerator<GENERATOR> {
858    fn finalize(self) -> Result<TokenStream2> {
859        self.inner.finalize(self.format_string)
860    }
861
862    fn string_fragment(&mut self, string: &str) -> Result<()> {
863        // Escape '{' and '} characters.
864        let format_string = string.replace('{', "{{").replace('}', "}}");
865
866        self.format_string.push_str(&format_string);
867        self.inner.string_fragment(string)
868    }
869
870    fn integer_conversion(
871        &mut self,
872        params: &FormatParams,
873        signed: bool,
874        type_width: u8, // in bits
875        expression: Arg,
876    ) -> Result<()> {
877        let ty = if signed {
878            format_ident!("i{type_width}")
879        } else {
880            format_ident!("u{type_width}")
881        };
882
883        let conversion = params.core_fmt_specifier()?;
884
885        match self.inner.integer_conversion(ty, expression)? {
886            Some(s) => self.format_string.push_str(&s),
887            None => self.format_string.push_str(&conversion),
888        }
889
890        Ok(())
891    }
892
893    fn string_conversion(&mut self, expression: Arg) -> Result<()> {
894        match self.inner.string_conversion(expression)? {
895            Some(s) => self.format_string.push_str(&s),
896            None => self.format_string.push_str("{}"),
897        }
898        Ok(())
899    }
900
901    fn char_conversion(&mut self, expression: Arg) -> Result<()> {
902        match self.inner.char_conversion(expression)? {
903            Some(s) => self.format_string.push_str(&s),
904            None => self.format_string.push_str("{}"),
905        }
906        Ok(())
907    }
908
909    fn untyped_conversion(&mut self, expression: Arg, params: &FormatParams) -> Result<()> {
910        self.inner.untyped_conversion(expression)?;
911        self.format_string.push_str(&params.core_fmt_specifier()?);
912        Ok(())
913    }
914}
915
916/// Generate code for a `pw_format` style proc macro that needs a [`core::fmt`] format string.
917///
918/// `generate_core_fmt` is a specialized version of [`generate`] which works with
919/// [`CoreFmtFormatMacroGenerator`]
920pub fn generate_core_fmt(
921    generator: impl CoreFmtFormatMacroGenerator,
922    format_and_args: FormatAndArgs,
923) -> core::result::Result<TokenStream2, syn::Error> {
924    let generator = CoreFmtGenerator {
925        inner: generator,
926        format_string: "".into(),
927    };
928    generate(generator, format_and_args)
929}