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