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