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