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