1use 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#[derive(Debug)]
63pub struct Error {
64 text: String,
65}
66
67impl Error {
68 pub fn new(text: &str) -> Self {
70 Self {
71 text: text.to_string(),
72 }
73 }
74}
75
76pub type Result<T> = core::result::Result<T, Error>;
78
79#[derive(Clone, Debug, PartialEq)]
81pub struct FormatParams {
82 pub style: Style,
84
85 pub min_field_width: Option<u32>,
87
88 pub zero_padding: bool,
90
91 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 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
169impl 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
192pub trait FormatMacroGenerator {
206 fn finalize(self) -> Result<TokenStream2>;
211
212 fn string_fragment(&mut self, string: &str) -> Result<()>;
219
220 fn integer_conversion(
222 &mut self,
223 params: &FormatParams,
224 signed: bool,
225 type_width: u8, expression: Arg,
227 ) -> Result<()>;
228
229 fn string_conversion(&mut self, expression: Arg) -> Result<()>;
234
235 fn char_conversion(&mut self, expression: Arg) -> Result<()>;
237
238 fn untyped_conversion(&mut self, _expression: Arg, _params: &FormatParams) -> Result<()> {
240 Err(Error::new("untyped conversion (%v) not supported"))
241 }
242}
243
244#[derive(Clone, Debug)]
252pub enum Arg {
253 ExprCast(ExprCast),
255 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::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
290pub trait FormatStringParser {
292 fn parse_format_string(format_string: &str) -> std::result::Result<FormatString, String>;
294}
295
296#[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#[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#[derive(Debug)]
327pub struct FormatAndArgs {
328 format_string: LitStr,
329 parsed: FormatString,
330 args: VecDeque<Arg>,
331}
332
333#[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 VecDeque::new()
370 } else {
371 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
396fn 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
402fn 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 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(¶ms, spec.primitive == Primitive::Integer, bits, arg)
448 }
449 Primitive::String => {
450 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, ¶ms)
475 }
476
477 Primitive::Float => {
478 Err(Error::new("Floating point numbers are not supported."))
480 }
481
482 Primitive::Pointer => Err(Error::new("Pointer types are not supported.")),
484 }
485}
486
487pub 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
526pub trait PrintfFormatMacroGenerator {
542 fn finalize(
547 self,
548 format_string_fragments: &[PrintfFormatStringFragment],
549 ) -> Result<TokenStream2>;
550
551 fn string_fragment(&mut self, string: &str) -> Result<()>;
562
563 fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>;
568
569 fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
577
578 fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
583
584 fn untyped_conversion(&mut self, _expression: Arg) -> Result<()> {
586 Err(Error::new("untyped conversion not supported"))
587 }
588}
589
590pub enum PrintfFormatStringFragment {
617 String(String),
619
620 Expr {
622 arg: Arg,
624 format_trait: Ident,
628 },
629}
630
631impl PrintfFormatStringFragment {
632 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
667struct PrintfGenerator<GENERATOR: PrintfFormatMacroGenerator> {
670 inner: GENERATOR,
671 format_string_fragments: Vec<PrintfFormatStringFragment>,
672}
673
674impl<GENERATOR: PrintfFormatMacroGenerator> PrintfGenerator<GENERATOR> {
675 fn append_format_string(&mut self, format_string: &str) {
677 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 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 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, 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
787pub 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
802pub trait CoreFmtFormatMacroGenerator {
813 fn finalize(self, format_string: String) -> Result<TokenStream2>;
818
819 fn string_fragment(&mut self, string: &str) -> Result<()>;
831
832 fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>;
834
835 fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
837
838 fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>>;
840
841 fn untyped_conversion(&mut self, _expression: Arg) -> Result<()> {
843 Err(Error::new("untyped conversion ({}) not supported"))
844 }
845}
846
847struct 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 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, 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(¶ms.core_fmt_specifier()?);
909 Ok(())
910 }
911}
912
913pub 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}