1use std::collections::HashSet;
16
17#[cfg(feature = "proc_macro")]
18use quote::{quote, ToTokens};
19
20use crate::{core_fmt, printf};
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum Primitive {
25 Integer,
27
28 Unsigned,
30
31 Float,
33
34 String,
36
37 Character,
39
40 Pointer,
42
43 Untyped,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub enum Style {
50 None,
52
53 Octal,
55
56 Hex,
58
59 UpperHex,
61
62 Exponential,
64
65 UpperExponential,
67
68 Pointer,
70
71 Debug,
73
74 HexDebug,
76
77 UpperHexDebug,
79
80 Binary,
85}
86
87#[cfg(feature = "proc_macro")]
89impl ToTokens for Style {
90 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
91 let new_tokens = match self {
92 Style::None => quote!(pw_format::Style::None),
93 Style::Octal => quote!(pw_format::Style::Octal),
94 Style::Hex => quote!(pw_format::Style::Hex),
95 Style::UpperHex => quote!(pw_format::Style::UpperHex),
96 Style::Exponential => quote!(pw_format::Style::Exponential),
97 Style::UpperExponential => quote!(pw_format::Style::UpperExponential),
98 Style::Debug => quote!(pw_format::Style::Debug),
99 Style::HexDebug => quote!(pw_format::Style::HexDebug),
100 Style::UpperHexDebug => quote!(pw_format::Style::UpperHexDebug),
101 Style::Pointer => quote!(pw_format::Style::Pointer),
102 Style::Binary => quote!(pw_format::Style::Binary),
103 };
104 new_tokens.to_tokens(tokens);
105 }
106}
107
108#[derive(Clone, Debug, Hash, PartialEq, Eq)]
110pub enum Flag {
111 LeftJustify,
113
114 ForceSign,
116
117 SpaceSign,
119
120 AlternateSyntax,
122
123 LeadingZeros,
125}
126
127#[derive(Clone, Debug, PartialEq, Eq)]
129pub enum MinFieldWidth {
130 None,
132
133 Fixed(u32),
135
136 Variable,
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
145pub enum Precision {
146 None,
148
149 Fixed(u32),
151
152 Variable,
154}
155
156#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158pub enum Length {
159 Char,
161
162 Short,
164
165 Long,
167
168 LongLong,
170
171 LongDouble,
173
174 IntMax,
176
177 Size,
179
180 PointerDiff,
182}
183
184#[derive(Clone, Copy, Debug, PartialEq, Eq)]
186pub enum Alignment {
187 None,
189
190 Left,
192
193 Center,
195
196 Right,
198}
199
200#[derive(Clone, Debug, PartialEq, Eq)]
204pub enum Argument {
205 None,
207
208 Positional(usize),
210
211 Named(String),
213}
214
215#[derive(Clone, Debug, PartialEq, Eq)]
217pub struct ConversionSpec {
218 pub argument: Argument,
220 pub fill: char,
222 pub alignment: Alignment,
224 pub flags: HashSet<Flag>,
226 pub min_field_width: MinFieldWidth,
228 pub precision: Precision,
230 pub length: Option<Length>,
232 pub primitive: Primitive,
234 pub style: Style,
236}
237
238impl ConversionSpec {
239 #[must_use]
241 pub fn to_printf(&self) -> String {
242 let mut s = String::from("%");
243 if self.flags.contains(&Flag::LeftJustify) {
244 s.push('-');
245 }
246 if self.flags.contains(&Flag::ForceSign) {
247 s.push('+');
248 }
249 if self.flags.contains(&Flag::SpaceSign) {
250 s.push(' ');
251 }
252 if self.flags.contains(&Flag::AlternateSyntax) {
253 s.push('#');
254 }
255 if self.flags.contains(&Flag::LeadingZeros) {
256 s.push('0');
257 }
258
259 match self.min_field_width {
260 MinFieldWidth::None => {}
261 MinFieldWidth::Fixed(w) => s.push_str(&w.to_string()),
262 MinFieldWidth::Variable => s.push('*'),
263 }
264
265 match self.precision {
266 Precision::None => {}
267 Precision::Fixed(p) => s.push_str(&format!(".{p}")),
268 Precision::Variable => s.push_str(".*"),
269 }
270
271 if let Some(length) = self.length {
272 s.push_str(match length {
273 Length::Char => "hh",
274 Length::Short => "h",
275 Length::Long => "l",
276 Length::LongLong => "ll",
277 Length::LongDouble => "L",
278 Length::IntMax => "j",
279 Length::Size => "z",
280 Length::PointerDiff => "t",
281 });
282 }
283
284 let type_char = match (self.primitive, self.style) {
285 (Primitive::Integer, _) => 'd',
286 (Primitive::Unsigned, Style::Octal) => 'o',
287 (Primitive::Unsigned, Style::Hex) => 'x',
288 (Primitive::Unsigned, Style::UpperHex) => 'X',
289 (Primitive::Unsigned, _) => 'u',
290 (Primitive::Float, Style::Exponential) => 'e',
291 (Primitive::Float, Style::UpperExponential) => 'E',
292 (Primitive::Float, _) => 'f',
293 (Primitive::Character, _) => 'c',
294 (Primitive::String, _) => 's',
295 (Primitive::Pointer, _) => 'p',
296 (Primitive::Untyped, _) => 'v',
297 };
298 s.push(type_char);
299 s
300 }
301}
302
303#[derive(Clone, Debug, PartialEq, Eq)]
305pub enum FormatFragment {
306 Literal(String),
308
309 Conversion(ConversionSpec),
311}
312
313impl FormatFragment {
314 fn try_append<'a>(&mut self, fragment: &'a FormatFragment) -> Option<&'a FormatFragment> {
318 let Self::Literal(literal_fragment) = &fragment else {
319 return Some(fragment);
320 };
321
322 let Self::Literal(literal_self) = self else {
323 return Some(fragment);
324 };
325
326 literal_self.push_str(literal_fragment);
327
328 None
329 }
330}
331
332#[derive(Debug, Clone, PartialEq)]
334pub enum Arg {
335 Int(i64),
337 Uint(u64),
339 Float(f64),
341 Str(String),
343 Char(char),
345 Ptr(usize),
347}
348
349#[derive(Debug, Clone, Copy, PartialEq, Eq)]
351pub enum FormatStyle {
352 Printf,
354 CoreFmt,
356}
357
358pub trait FormatError {
360 type Error;
362
363 fn format_error(&self, spec: &ConversionSpec, error: &Self::Error) -> String;
365
366 fn format_missing(&self, spec: &ConversionSpec) -> String;
368
369 fn format_type_error(&self, spec: &ConversionSpec, arg: &Arg) -> String;
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
376struct DefaultFormatter;
377
378impl FormatError for DefaultFormatter {
379 type Error = core::convert::Infallible;
380
381 fn format_error(&self, spec: &ConversionSpec, _error: &core::convert::Infallible) -> String {
382 spec.to_printf()
383 }
384 fn format_missing(&self, spec: &ConversionSpec) -> String {
385 spec.to_printf()
386 }
387 fn format_type_error(&self, spec: &ConversionSpec, _arg: &Arg) -> String {
388 spec.to_printf()
389 }
390}
391
392#[derive(Clone, Debug, PartialEq, Eq)]
394pub struct FormatString {
395 pub fragments: Vec<FormatFragment>,
397}
398
399impl FormatString {
400 #[must_use]
402 pub fn format(&self, args: &[Arg], style: FormatStyle) -> String {
403 let result_args: Vec<Result<Arg, core::convert::Infallible>> =
404 args.iter().map(|arg| Ok(arg.clone())).collect();
405
406 self.format_with_errors(&result_args, style, &DefaultFormatter)
407 }
408
409 pub fn format_with_errors<FE: FormatError>(
413 &self,
414 args: &[Result<Arg, FE::Error>],
415 style: FormatStyle,
416 error_formatter: &FE,
417 ) -> String {
418 let mut output = String::new();
419 let mut args_iter = args.iter();
420
421 for fragment in &self.fragments {
422 self.format_fragment(
423 fragment,
424 &mut args_iter,
425 style,
426 error_formatter,
427 &mut output,
428 );
429 }
430
431 output
432 }
433
434 fn format_fragment<'a, FE: FormatError>(
435 &self,
436 fragment: &FormatFragment,
437 args: &mut impl Iterator<Item = &'a Result<Arg, FE::Error>>,
438 style: FormatStyle,
439 error_formatter: &FE,
440 output: &mut String,
441 ) where
442 FE::Error: 'a,
443 {
444 let spec = match fragment {
445 FormatFragment::Conversion(spec) => spec,
446 FormatFragment::Literal(s) => {
447 output.push_str(s);
448 return;
449 }
450 };
451
452 let Some(decoded) = args.next() else {
453 output.push_str(&error_formatter.format_missing(spec));
454 return;
455 };
456
457 let arg = match decoded {
458 Ok(arg) => arg,
459 Err(err) => {
460 output.push_str(&error_formatter.format_error(spec, err));
461 return;
462 }
463 };
464
465 let mut formatted = String::new();
466 match self.format_value(spec, arg, style, &mut formatted) {
467 Ok(()) => output.push_str(&formatted),
468 Err(_) => {
469 output.push_str(&error_formatter.format_type_error(spec, arg));
470 }
471 }
472 }
473
474 pub fn parse_printf(s: &str) -> Result<Self, String> {
476 let (rest, result) = printf::format_string(s)
478 .map_err(|e| format!("Failed to parse format string \"{s}\": {e}"))?;
479
480 if !rest.is_empty() {
482 return Err(format!(
483 "Failed to parse format string fragment: \"{rest}\""
484 ));
485 }
486
487 Ok(result)
488 }
489
490 pub fn parse_core_fmt(s: &str) -> Result<Self, String> {
492 let (rest, result) = core_fmt::format_string(s)
494 .map_err(|e| format!("Failed to parse format string \"{s}\": {e}"))?;
495
496 if !rest.is_empty() {
498 return Err(format!("Failed to parse format string: \"{rest}\""));
499 }
500
501 Ok(result)
502 }
503
504 pub(crate) fn from_fragments(fragments: &[FormatFragment]) -> Self {
511 Self {
512 fragments: fragments
513 .iter()
514 .fold(Vec::<_>::new(), |mut fragments, fragment| {
515 let Some(last) = fragments.last_mut() else {
517 fragments.push((*fragment).clone());
519 return fragments;
520 };
521 if let Some(fragment) = last.try_append(fragment) {
522 fragments.push((*fragment).clone());
524 };
525 fragments
526 }),
527 }
528 }
529
530 fn format_value(
531 &self,
532 spec: &ConversionSpec,
533 arg: &Arg,
534 style: FormatStyle,
535 output: &mut String,
536 ) -> Result<(), String> {
537 match (spec.primitive, arg) {
538 (Primitive::Integer, Arg::Int(v)) => self.format_int(*v, spec, style, output),
539 (Primitive::Unsigned, Arg::Uint(v)) => self.format_uint(*v, spec, style, output),
540 (Primitive::Float, Arg::Float(v)) => self.format_float(*v, spec, style, output),
541 (Primitive::String, Arg::Str(v)) => self.format_str(v, spec, style, output),
542 (Primitive::Character, Arg::Char(v)) => self.format_char(*v, spec, style, output),
543 (Primitive::Pointer, Arg::Ptr(v)) => self.format_ptr(*v, spec, style, output),
544 (Primitive::Untyped, _) => self.format_untyped(spec, arg, style, output),
545 _ => Err(format!(
546 "Mismatched type: expected {:?}, got {:?}",
547 spec.primitive, arg
548 )),
549 }
550 }
551
552 fn format_untyped(
553 &self,
554 spec: &ConversionSpec,
555 arg: &Arg,
556 style: FormatStyle,
557 output: &mut String,
558 ) -> Result<(), String> {
559 match arg {
560 Arg::Int(v) => self.format_int(*v, spec, style, output),
561 Arg::Uint(v) => self.format_uint(*v, spec, style, output),
562 Arg::Float(v) => self.format_float(*v, spec, style, output),
563 Arg::Str(v) => self.format_str(v, spec, style, output),
564 Arg::Char(v) => self.format_char(*v, spec, style, output),
565 Arg::Ptr(v) => self.format_ptr(*v, spec, style, output),
566 }
567 }
568
569 fn format_int_common(
570 &self,
571 v: u64,
572 sign: &str,
573 spec: &ConversionSpec,
574 output: &mut String,
575 ) -> Result<(), String> {
576 let (base_prefix, mut value) = match spec.style {
577 Style::Hex | Style::Pointer => ("0x", format!("{:x}", v)),
578 Style::UpperHex => ("0X", format!("{:X}", v)),
579 Style::Octal => ("0", format!("{:o}", v)),
580 _ => ("", format!("{}", v)),
581 };
582
583 if let Precision::Fixed(p) = spec.precision {
584 while value.len() < p as usize {
585 value.insert(0, '0');
586 }
587 }
588
589 let mut prefix = sign.to_string();
590 if spec.flags.contains(&Flag::AlternateSyntax) || spec.style == Style::Pointer {
591 if !value.starts_with(base_prefix) {
593 prefix.push_str(base_prefix);
594 }
595 }
596
597 let s = self.apply_width_and_alignment(&prefix, &value, spec)?;
598 output.push_str(&s);
599 Ok(())
600 }
601
602 fn sign_prefix(&self, is_negative: bool, spec: &ConversionSpec) -> &'static str {
603 if is_negative {
604 "-"
605 } else if spec.flags.contains(&Flag::ForceSign) {
606 "+"
607 } else if spec.flags.contains(&Flag::SpaceSign) {
608 " "
609 } else {
610 ""
611 }
612 }
613
614 fn format_int(
615 &self,
616 v: i64,
617 spec: &ConversionSpec,
618 _style: FormatStyle,
619 output: &mut String,
620 ) -> Result<(), String> {
621 let sign = self.sign_prefix(v < 0, spec);
622 self.format_int_common(v.unsigned_abs(), sign, spec, output)
623 }
624
625 fn format_uint(
626 &self,
627 v: u64,
628 spec: &ConversionSpec,
629 _style: FormatStyle,
630 output: &mut String,
631 ) -> Result<(), String> {
632 self.format_int_common(v, "", spec, output)
633 }
634
635 fn format_float(
636 &self,
637 v: f64,
638 spec: &ConversionSpec,
639 style: FormatStyle,
640 output: &mut String,
641 ) -> Result<(), String> {
642 let abs_v = v.abs();
643 let value = match spec.precision {
644 Precision::Fixed(p) => format!("{:.1$}", abs_v, p as usize),
645 _ => match style {
646 FormatStyle::Printf => format!("{:.6}", abs_v),
647 FormatStyle::CoreFmt => format!("{}", abs_v),
648 },
649 };
650 let prefix = self.sign_prefix(v < 0.0 || v.is_sign_negative(), spec);
651
652 let s = self.apply_width_and_alignment(prefix, &value, spec)?;
653 output.push_str(&s);
654 Ok(())
655 }
656
657 fn format_str(
658 &self,
659 v: &str,
660 spec: &ConversionSpec,
661 _style: FormatStyle,
662 output: &mut String,
663 ) -> Result<(), String> {
664 let mut value = v.to_string();
665 if let Precision::Fixed(p) = spec.precision {
666 value.truncate(p as usize);
667 }
668 let s = self.apply_width_and_alignment("", &value, spec)?;
669 output.push_str(&s);
670 Ok(())
671 }
672
673 fn format_char(
674 &self,
675 v: char,
676 spec: &ConversionSpec,
677 _style: FormatStyle,
678 output: &mut String,
679 ) -> Result<(), String> {
680 let value = v.to_string();
681 let s = self.apply_width_and_alignment("", &value, spec)?;
682 output.push_str(&s);
683 Ok(())
684 }
685
686 fn format_ptr(
687 &self,
688 v: usize,
689 spec: &ConversionSpec,
690 _style: FormatStyle,
691 output: &mut String,
692 ) -> Result<(), String> {
693 self.format_int_common(v as u64, "", spec, output)
694 }
695
696 fn apply_width_and_alignment(
697 &self,
698 prefix: &str,
699 value: &str,
700 spec: &ConversionSpec,
701 ) -> Result<String, String> {
702 let MinFieldWidth::Fixed(w) = spec.min_field_width else {
705 return Ok(format!("{}{}", prefix, value));
706 };
707
708 let w = w as usize;
709 let total_len = prefix.len() + value.len();
710
711 if total_len >= w {
713 return Ok(format!("{}{}", prefix, value));
714 }
715
716 let pad_len = w - total_len;
717 let ignore_zero = spec.flags.contains(&Flag::LeftJustify)
718 || (matches!(spec.precision, Precision::Fixed(_))
719 && matches!(spec.primitive, Primitive::Integer | Primitive::Unsigned));
720 let do_zero_fill = spec.flags.contains(&Flag::LeadingZeros) && !ignore_zero;
721 let is_left_aligned =
722 spec.alignment == Alignment::Left || spec.flags.contains(&Flag::LeftJustify);
723
724 let mut s = String::with_capacity(w);
725 if is_left_aligned {
726 s.push_str(prefix);
728 s.push_str(value);
729 for _ in 0..pad_len {
730 s.push(spec.fill);
731 }
732 } else {
733 if do_zero_fill {
734 s.push_str(prefix);
736 for _ in 0..pad_len {
737 s.push('0');
738 }
739 } else {
740 for _ in 0..pad_len {
742 s.push(spec.fill);
743 }
744 s.push_str(prefix);
745 }
746 s.push_str(value);
747 }
748 Ok(s)
749 }
750}