1use core::marker::PhantomData;
37use std::collections::VecDeque;
38
39use proc_macro2::Ident;
40use quote::{format_ident, quote, ToTokens};
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#[derive(Debug)]
63pub struct Error {
64 text: String,
65}
66
67impl Error {
68 #[must_use]
70 pub fn new(text: &str) -> Self {
71 Self {
72 text: text.to_string(),
73 }
74 }
75}
76
77pub type Result<T> = core::result::Result<T, Error>;
79
80#[derive(Clone, Debug, PartialEq)]
82pub struct FormatParams {
83 pub style: Style,
85
86 pub min_field_width: Option<u32>,
88
89 pub zero_padding: bool,
91
92 pub alternate_syntax: bool,
94}
95
96impl FormatParams {
97 fn printf_format_trait(&self) -> Result<Ident> {
98 match self.style {
99 Style::None => Ok(format_ident!("PrintfFormatter")),
100 Style::Hex => Ok(format_ident!("PrintfHexFormatter")),
101 Style::UpperHex => Ok(format_ident!("PrintfUpperHexFormatter")),
102 _ => Err(Error::new(&format!(
103 "formatting untyped conversions with {:?} style is unsupported",
104 self.style
105 ))),
106 }
107 }
108
109 fn field_params(&self) -> String {
110 let (zero_pad, min_field_width) = match self.min_field_width {
111 None => ("", "".to_string()),
112 Some(min_field_width) => (
113 if self.zero_padding { "0" } else { "" },
114 format!("{min_field_width}"),
115 ),
116 };
117
118 let alternate_syntax = if self.alternate_syntax { "#" } else { "" };
119
120 format!("{alternate_syntax}{zero_pad}{min_field_width}")
121 }
122
123 fn core_fmt_specifier(&self) -> Result<String> {
124 if self.style == Style::None && self.min_field_width.is_none() {
126 return Ok("{}".to_string());
127 }
128
129 let format = match self.style {
130 Style::None => "",
131 Style::Octal => "o",
132 Style::Hex => "x",
133 Style::UpperHex => "X",
134 _ => {
135 return Err(Error::new(&format!(
136 "formatting untyped conversions with {:?} style is unsupported",
137 self.style
138 )));
139 }
140 };
141
142 let field_params = self.field_params();
143
144 Ok(format!("{{:{field_params}{format}}}"))
145 }
146}
147
148impl TryFrom<&ConversionSpec> for FormatParams {
149 type Error = Error;
150 fn try_from(spec: &ConversionSpec) -> core::result::Result<Self, Self::Error> {
151 let min_field_width = match spec.min_field_width {
152 MinFieldWidth::None => None,
153 MinFieldWidth::Fixed(len) => Some(len),
154 MinFieldWidth::Variable => {
155 return Err(Error::new(
156 "Variable width '*' string formats are not supported.",
157 ));
158 }
159 };
160
161 Ok(FormatParams {
162 style: spec.style,
163 min_field_width,
164 zero_padding: spec.flags.contains(&Flag::LeadingZeros),
165 alternate_syntax: spec.flags.contains(&Flag::AlternateSyntax),
166 })
167 }
168}
169
170impl ToTokens for FormatParams {
172 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
173 let style = self.style;
174 let min_field_width = match self.min_field_width {
175 None => quote! {None},
176 Some(val) => quote! {Some(#val)},
177 };
178 let zero_padding = self.zero_padding;
179 let alternate_syntax = self.alternate_syntax;
180
181 quote! {
182 pw_format::macros::FormatParams {
183 style: #style,
184 min_field_width: #min_field_width,
185 zero_padding: #zero_padding,
186 alternate_syntax: #alternate_syntax,
187 }
188 }
189 .to_tokens(tokens)
190 }
191}
192
193pub trait FormatMacroGenerator {
207 fn finalize(self) -> Result<TokenStream2>;
212
213 fn string_fragment(&mut self, string: &str) -> Result<()>;
220
221 fn integer_conversion(
223 &mut self,
224 params: &FormatParams,
225 signed: bool,
226 type_width: u8, expression: Arg,
228 ) -> Result<()>;
229
230 fn string_conversion(&mut self, expression: Arg) -> Result<()>;
235
236 fn char_conversion(&mut self, expression: Arg) -> Result<()>;
238
239 fn untyped_conversion(&mut self, _expression: Arg, _params: &FormatParams) -> Result<()> {
241 Err(Error::new("untyped conversion (%v) not supported"))
242 }
243}
244
245#[derive(Clone, Debug)]
253pub enum Arg {
254 ExprCast(ExprCast),
256 Expr(Expr),
258}
259
260impl Arg {
261 fn parse_expr(expr: Expr) -> syn::parse::Result<Self> {
262 match expr.clone() {
263 Expr::Cast(cast) => Ok(Self::ExprCast(cast)),
264
265 Expr::Paren(paren) => Self::parse_expr(*paren.expr),
269 Expr::Group(group) => Self::parse_expr(*group.expr),
270
271 _ => Ok(Self::Expr(expr)),
272 }
273 }
274}
275
276impl Parse for Arg {
277 fn parse(input: ParseStream) -> syn::parse::Result<Self> {
278 Self::parse_expr(input.parse::<Expr>()?)
279 }
280}
281
282impl ToTokens for Arg {
283 fn to_tokens(&self, tokens: &mut TokenStream2) {
284 match self {
285 Self::Expr(expr) => expr.to_tokens(tokens),
286 Self::ExprCast(cast) => cast.to_tokens(tokens),
287 }
288 }
289}
290
291pub trait FormatStringParser {
293 fn parse_format_string(format_string: &str) -> core::result::Result<FormatString, String>;
295}
296
297#[derive(Debug)]
299pub struct PrintfFormatStringParser;
300impl FormatStringParser for PrintfFormatStringParser {
301 fn parse_format_string(format_string: &str) -> core::result::Result<FormatString, String> {
302 FormatString::parse_printf(format_string)
303 }
304}
305
306#[derive(Debug)]
308pub struct CoreFmtFormatStringParser;
309impl FormatStringParser for CoreFmtFormatStringParser {
310 fn parse_format_string(format_string: &str) -> core::result::Result<FormatString, String> {
311 FormatString::parse_core_fmt(format_string)
312 }
313}
314
315#[derive(Debug)]
328pub struct FormatAndArgs {
329 format_string: LitStr,
330 parsed: FormatString,
331 args: VecDeque<Arg>,
332}
333
334#[derive(Debug)]
344pub struct FormatAndArgsFlavor<T: FormatStringParser> {
345 format_and_args: FormatAndArgs,
346 phantom: PhantomData<T>,
347}
348
349impl<T: FormatStringParser> From<FormatAndArgsFlavor<T>> for FormatAndArgs {
350 fn from(val: FormatAndArgsFlavor<T>) -> Self {
351 val.format_and_args
352 }
353}
354
355impl<T: FormatStringParser> Parse for FormatAndArgsFlavor<T> {
356 fn parse(input: ParseStream) -> syn::parse::Result<Self> {
357 let punctuated =
358 Punctuated::<LitStr, keywords::PW_FMT_CONCAT>::parse_separated_nonempty(input)?;
359 let span = punctuated.span();
360 let format_string = LitStr::new(
361 &punctuated.into_iter().fold(String::new(), |mut acc, s| {
362 acc.push_str(&s.value());
363 acc
364 }),
365 span,
366 );
367
368 let args = if input.is_empty() {
369 VecDeque::new()
371 } else {
372 input.parse::<Token![,]>()?;
374
375 let punctuated = Punctuated::<Arg, Token![,]>::parse_terminated(input)?;
376 punctuated.into_iter().collect()
377 };
378
379 let parsed = T::parse_format_string(&format_string.value()).map_err(|e| {
380 syn::Error::new_spanned(
381 format_string.to_token_stream(),
382 format!("Error parsing format string {e}"),
383 )
384 })?;
385
386 Ok(FormatAndArgsFlavor {
387 format_and_args: FormatAndArgs {
388 format_string,
389 parsed,
390 args,
391 },
392 phantom: PhantomData,
393 })
394 }
395}
396
397fn next_arg(spec: &ConversionSpec, args: &mut VecDeque<Arg>) -> Result<Arg> {
399 args.pop_front()
400 .ok_or_else(|| Error::new(&format!("No argument given for {spec:?}")))
401}
402
403fn handle_conversion(
408 generator: &mut dyn FormatMacroGenerator,
409 spec: &ConversionSpec,
410 args: &mut VecDeque<Arg>,
411) -> Result<()> {
412 match spec.primitive {
413 Primitive::Integer | Primitive::Unsigned => {
414 if spec.min_field_width == MinFieldWidth::Variable {
416 return Err(Error::new(
417 "Variable width '*' integer formats are not supported.",
418 ));
419 }
420
421 if spec.precision == Precision::Variable {
422 return Err(Error::new(
423 "Variable precision '*' integer formats are not supported.",
424 ));
425 }
426
427 if spec.style == Style::Binary {
428 return Err(Error::new("Binary output style is not supported."));
429 }
430
431 let arg = next_arg(spec, args)?;
432 let bits = match spec.length.unwrap_or(Length::Long) {
433 Length::Char => 8,
434 Length::Short => 16,
435 Length::Long => 32,
436 Length::LongLong => 64,
437 Length::IntMax => 64,
438 Length::Size => 32,
439 Length::PointerDiff => 32,
440 Length::LongDouble => {
441 return Err(Error::new(
442 "Long double length parameter invalid for integer formats",
443 ));
444 }
445 };
446 let params = spec.try_into()?;
447
448 generator.integer_conversion(¶ms, spec.primitive == Primitive::Integer, bits, arg)
449 }
450 Primitive::String => {
451 if spec.min_field_width == MinFieldWidth::Variable {
453 return Err(Error::new(
454 "Variable width '*' string formats are not supported.",
455 ));
456 }
457
458 if spec.precision == Precision::Variable {
459 return Err(Error::new(
460 "Variable precision '*' string formats are not supported.",
461 ));
462 }
463
464 let arg = next_arg(spec, args)?;
465 generator.string_conversion(arg)
466 }
467 Primitive::Character => {
468 let arg = next_arg(spec, args)?;
469 generator.char_conversion(arg)
470 }
471
472 Primitive::Untyped => {
473 let arg = next_arg(spec, args)?;
474 let params = spec.try_into()?;
475 generator.untyped_conversion(arg, ¶ms)
476 }
477
478 Primitive::Float => {
479 Err(Error::new("Floating point numbers are not supported."))
481 }
482
483 Primitive::Pointer => Err(Error::new("Pointer types are not supported.")),
485 }
486}
487
488pub fn generate(
493 mut generator: impl FormatMacroGenerator,
494 format_and_args: FormatAndArgs,
495) -> core::result::Result<TokenStream2, syn::Error> {
496 let mut args = format_and_args.args;
497 let mut errors = Vec::new();
498
499 for fragment in format_and_args.parsed.fragments {
500 let result = match fragment {
501 FormatFragment::Conversion(spec) => handle_conversion(&mut generator, &spec, &mut args),
502 FormatFragment::Literal(string) => generator.string_fragment(&string),
503 };
504 if let Err(e) = result {
505 errors.push(syn::Error::new_spanned(
506 format_and_args.format_string.to_token_stream(),
507 e.text,
508 ));
509 }
510 }
511
512 if !errors.is_empty() {
513 return Err(errors
514 .into_iter()
515 .reduce(|mut accumulated_errors, error| {
516 accumulated_errors.combine(error);
517 accumulated_errors
518 })
519 .expect("errors should not be empty"));
520 }
521
522 generator.finalize().map_err(|e| {
523 syn::Error::new_spanned(format_and_args.format_string.to_token_stream(), e.text)
524 })
525}
526
527pub trait PrintfFormatMacroGenerator {
543 fn finalize(
548 self,
549 format_string_fragments: &[PrintfFormatStringFragment],
550 ) -> Result<TokenStream2>;
551
552 fn string_fragment(&mut self, string: &str) -> Result<()>;
563
564 fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>;
569
570 fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
578
579 fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
584
585 fn untyped_conversion(&mut self, _expression: Arg) -> Result<()> {
587 Err(Error::new("untyped conversion not supported"))
588 }
589}
590
591pub enum PrintfFormatStringFragment {
618 String(String),
620
621 Expr {
623 arg: Arg,
625 format_trait: Ident,
629 },
630}
631
632impl PrintfFormatStringFragment {
633 pub fn as_token_stream(&self, printf_formatter_trait_location: &str) -> Result<TokenStream2> {
635 let crate_name = format_ident!("{}", printf_formatter_trait_location);
636 match self {
637 Self::String(s) => Ok(quote! {#s}),
638 #[cfg(not(feature = "nightly_tait"))]
639 Self::Expr { arg, format_trait } => {
640 let Arg::ExprCast(cast) = arg else {
641 return Err(Error::new(&format!(
642 "Expected argument to untyped format (%v/{{}}) to be a cast expression (e.g. x as i32), but found {}.",
643 arg.to_token_stream(),
644 )));
645 };
646 let ty = &cast.ty;
647 Ok(quote! {
648 {
649 use #crate_name::#format_trait;
650 <#ty as #format_trait>::FORMAT_ARG
651 }
652 })
653 }
654 #[cfg(feature = "nightly_tait")]
655 Self::Expr { arg, format_trait } => Ok(quote! {
656 {
657 use #crate_name::#format_trait;
658 type T = impl #format_trait;
659 let _: &T = &(#arg);
660 let arg = <T as #format_trait>::FORMAT_ARG;
661 arg
662 }
663 }),
664 }
665 }
666}
667
668struct PrintfGenerator<GENERATOR: PrintfFormatMacroGenerator> {
671 inner: GENERATOR,
672 format_string_fragments: Vec<PrintfFormatStringFragment>,
673}
674
675impl<GENERATOR: PrintfFormatMacroGenerator> PrintfGenerator<GENERATOR> {
676 fn append_format_string(&mut self, format_string: &str) {
678 if let PrintfFormatStringFragment::String(s) = self
680 .format_string_fragments
681 .last_mut()
682 .expect("format_string_fragments always has at least one entry")
683 {
684 s.push_str(format_string)
685 } else {
686 self.format_string_fragments
688 .push(PrintfFormatStringFragment::String(
689 format_string.to_string(),
690 ));
691 }
692 }
693}
694
695impl<GENERATOR: PrintfFormatMacroGenerator> FormatMacroGenerator for PrintfGenerator<GENERATOR> {
696 fn finalize(self) -> Result<TokenStream2> {
697 self.inner.finalize(&self.format_string_fragments)
698 }
699
700 fn string_fragment(&mut self, string: &str) -> Result<()> {
701 let format_string = string.replace('%', "%%");
703
704 self.append_format_string(&format_string);
705 self.inner.string_fragment(string)
706 }
707
708 fn integer_conversion(
709 &mut self,
710 params: &FormatParams,
711 signed: bool,
712 type_width: u8, expression: Arg,
714 ) -> Result<()> {
715 let length_modifier = match type_width {
716 8 => "hh",
717 16 => "h",
718 32 => "",
719 64 => "ll",
720 _ => {
721 return Err(Error::new(&format!(
722 "printf backend does not support {type_width} bit field width"
723 )));
724 }
725 };
726
727 let (conversion, ty) = match params.style {
728 Style::None => {
729 if signed {
730 ("d", format_ident!("i{type_width}"))
731 } else {
732 ("u", format_ident!("u{type_width}"))
733 }
734 }
735 Style::Octal => ("o", format_ident!("u{type_width}")),
736 Style::Hex => ("x", format_ident!("u{type_width}")),
737 Style::UpperHex => ("X", format_ident!("u{type_width}")),
738 _ => {
739 return Err(Error::new(&format!(
740 "printf backend does not support formatting integers with {:?} style",
741 params.style
742 )));
743 }
744 };
745
746 match self.inner.integer_conversion(ty, expression)? {
747 Some(s) => self.append_format_string(&s),
748 None => self.append_format_string(&format!(
749 "%{}{}{}",
750 params.field_params(),
751 length_modifier,
752 conversion
753 )),
754 }
755
756 Ok(())
757 }
758
759 fn string_conversion(&mut self, expression: Arg) -> Result<()> {
760 match self.inner.string_conversion(expression)? {
761 Some(s) => self.append_format_string(&s),
762 None => self.append_format_string("%s"),
763 }
764 Ok(())
765 }
766
767 fn char_conversion(&mut self, expression: Arg) -> Result<()> {
768 match self.inner.char_conversion(expression)? {
769 Some(s) => self.append_format_string(&s),
770 None => self.append_format_string("%c"),
771 }
772 Ok(())
773 }
774
775 fn untyped_conversion(&mut self, expression: Arg, params: &FormatParams) -> Result<()> {
776 self.inner.untyped_conversion(expression.clone())?;
777
778 self.append_format_string(&format!("%{}", params.field_params()));
779 self.format_string_fragments
780 .push(PrintfFormatStringFragment::Expr {
781 arg: expression,
782 format_trait: params.printf_format_trait()?,
783 });
784 Ok(())
785 }
786}
787
788pub fn generate_printf(
793 generator: impl PrintfFormatMacroGenerator,
794 format_and_args: FormatAndArgs,
795) -> core::result::Result<TokenStream2, syn::Error> {
796 let generator = PrintfGenerator {
797 inner: generator,
798 format_string_fragments: vec![PrintfFormatStringFragment::String("".into())],
799 };
800 generate(generator, format_and_args)
801}
802
803pub trait CoreFmtFormatMacroGenerator {
814 fn finalize(self, format_string: String) -> Result<TokenStream2>;
819
820 fn string_fragment(&mut self, string: &str) -> Result<()>;
832
833 fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>;
835
836 fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
838
839 fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
841
842 fn untyped_conversion(&mut self, _expression: Arg) -> Result<()> {
844 Err(Error::new("untyped conversion ({}) not supported"))
845 }
846}
847
848struct CoreFmtGenerator<GENERATOR: CoreFmtFormatMacroGenerator> {
851 inner: GENERATOR,
852 format_string: String,
853}
854
855impl<GENERATOR: CoreFmtFormatMacroGenerator> FormatMacroGenerator for CoreFmtGenerator<GENERATOR> {
856 fn finalize(self) -> Result<TokenStream2> {
857 self.inner.finalize(self.format_string)
858 }
859
860 fn string_fragment(&mut self, string: &str) -> Result<()> {
861 let format_string = string.replace('{', "{{").replace('}', "}}");
863
864 self.format_string.push_str(&format_string);
865 self.inner.string_fragment(string)
866 }
867
868 fn integer_conversion(
869 &mut self,
870 params: &FormatParams,
871 signed: bool,
872 type_width: u8, expression: Arg,
874 ) -> Result<()> {
875 let ty = if signed {
876 format_ident!("i{type_width}")
877 } else {
878 format_ident!("u{type_width}")
879 };
880
881 let conversion = params.core_fmt_specifier()?;
882
883 match self.inner.integer_conversion(ty, expression)? {
884 Some(s) => self.format_string.push_str(&s),
885 None => self.format_string.push_str(&conversion),
886 }
887
888 Ok(())
889 }
890
891 fn string_conversion(&mut self, expression: Arg) -> Result<()> {
892 match self.inner.string_conversion(expression)? {
893 Some(s) => self.format_string.push_str(&s),
894 None => self.format_string.push_str("{}"),
895 }
896 Ok(())
897 }
898
899 fn char_conversion(&mut self, expression: Arg) -> Result<()> {
900 match self.inner.char_conversion(expression)? {
901 Some(s) => self.format_string.push_str(&s),
902 None => self.format_string.push_str("{}"),
903 }
904 Ok(())
905 }
906
907 fn untyped_conversion(&mut self, expression: Arg, params: &FormatParams) -> Result<()> {
908 self.inner.untyped_conversion(expression)?;
909 self.format_string.push_str(¶ms.core_fmt_specifier()?);
910 Ok(())
911 }
912}
913
914pub fn generate_core_fmt(
919 generator: impl CoreFmtFormatMacroGenerator,
920 format_and_args: FormatAndArgs,
921) -> core::result::Result<TokenStream2, syn::Error> {
922 let generator = CoreFmtGenerator {
923 inner: generator,
924 format_string: "".into(),
925 };
926 generate(generator, format_and_args)
927}