1use alloc::fmt;
4use bitflags::bitflags;
5use core::{
6 cmp,
7 iter::{Enumerate, Peekable},
8 str::FromStr,
9};
10use itertools::Itertools;
11use malachite_bigint::{BigInt, Sign};
12use num_traits::Signed;
13use rustpython_literal::{float, format::Case};
14
15use crate::wtf8::{CodePoint, Wtf8, Wtf8Buf};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum CFormatErrorType {
19 UnmatchedKeyParentheses,
20 MissingModuloSign,
21 UnsupportedFormatChar(CodePoint),
22 IncompleteFormat,
23 WidthTooBig,
24 PrecisionTooBig,
25 }
27
28pub type ParsingError = (CFormatErrorType, usize);
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub struct CFormatError {
33 pub typ: CFormatErrorType, pub index: usize,
35}
36
37impl fmt::Display for CFormatError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self.typ {
40 CFormatErrorType::UnmatchedKeyParentheses => write!(f, "incomplete format key"),
41 CFormatErrorType::IncompleteFormat => write!(f, "incomplete format"),
42 CFormatErrorType::UnsupportedFormatChar(c) => write!(
43 f,
44 "unsupported format character '{}' ({:#x}) at index {}",
45 c,
46 c.to_u32(),
47 self.index
48 ),
49 CFormatErrorType::WidthTooBig => write!(f, "width too big"),
50 CFormatErrorType::PrecisionTooBig => write!(f, "precision too big"),
51 _ => write!(f, "unexpected error parsing format string"),
52 }
53 }
54}
55
56pub type CFormatConversion = super::format::FormatConversion;
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum CFormatContext {
60 Str,
61 Bytes,
62}
63
64#[derive(Debug, PartialEq, Eq, Clone, Copy)]
65#[repr(u8)]
66pub enum CNumberType {
67 DecimalD = b'd',
68 DecimalI = b'i',
69 DecimalU = b'u',
70 Octal = b'o',
71 HexLower = b'x',
72 HexUpper = b'X',
73}
74
75#[derive(Debug, PartialEq, Eq, Clone, Copy)]
76#[repr(u8)]
77pub enum CFloatType {
78 ExponentLower = b'e',
79 ExponentUpper = b'E',
80 PointDecimalLower = b'f',
81 PointDecimalUpper = b'F',
82 GeneralLower = b'g',
83 GeneralUpper = b'G',
84}
85
86impl CFloatType {
87 const fn case(self) -> Case {
88 match self {
89 Self::ExponentLower | Self::PointDecimalLower | Self::GeneralLower => Case::Lower,
90 Self::ExponentUpper | Self::PointDecimalUpper | Self::GeneralUpper => Case::Upper,
91 }
92 }
93}
94
95#[derive(Debug, PartialEq, Eq, Clone, Copy)]
96#[repr(u8)]
97pub enum CCharacterType {
98 Character = b'c',
99}
100
101#[derive(Debug, PartialEq, Eq, Clone, Copy)]
102pub enum CFormatType {
103 Number(CNumberType),
104 Float(CFloatType),
105 Character(CCharacterType),
106 String(CFormatConversion),
107 Bytes,
108 Unsupported {
111 ch: CodePoint,
112 index: usize,
113 },
114}
115
116impl CFormatType {
117 #[must_use]
118 pub fn to_char(self) -> char {
119 match self {
120 Self::Number(x) => x as u8 as char,
121 Self::Float(x) => x as u8 as char,
122 Self::Character(x) => x as u8 as char,
123 Self::String(x) => x as u8 as char,
124 Self::Bytes => 'b',
125 Self::Unsupported { ch, .. } => ch.to_char_lossy(),
126 }
127 }
128}
129
130#[derive(Debug, PartialEq, Eq, Clone, Copy)]
131pub enum CFormatPrecision {
132 Quantity(CFormatQuantity),
133 Dot,
134}
135
136impl From<CFormatQuantity> for CFormatPrecision {
137 fn from(quantity: CFormatQuantity) -> Self {
138 Self::Quantity(quantity)
139 }
140}
141
142bitflags! {
143 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
144 pub struct CConversionFlags: u32 {
145 const ALTERNATE_FORM = 0b0000_0001;
146 const ZERO_PAD = 0b0000_0010;
147 const LEFT_ADJUST = 0b0000_0100;
148 const BLANK_SIGN = 0b0000_1000;
149 const SIGN_CHAR = 0b0001_0000;
150 }
151}
152
153impl CConversionFlags {
154 #[inline]
155 #[must_use]
156 pub const fn sign_string(&self) -> &'static str {
157 if self.contains(Self::SIGN_CHAR) {
158 "+"
159 } else if self.contains(Self::BLANK_SIGN) {
160 " "
161 } else {
162 ""
163 }
164 }
165}
166
167#[derive(Debug, PartialEq, Eq, Clone, Copy)]
168pub enum CFormatQuantity {
169 Amount(usize),
170 FromValuesTuple,
171}
172
173pub trait FormatBuf:
174 Extend<Self::Char> + Default + FromIterator<Self::Char> + From<String>
175{
176 type Char: FormatChar;
177
178 fn chars(&self) -> impl Iterator<Item = Self::Char>;
179
180 fn len(&self) -> usize;
181
182 fn is_empty(&self) -> bool {
183 self.len() == 0
184 }
185
186 #[must_use]
187 fn concat(self, other: Self) -> Self;
188}
189
190pub trait FormatChar: Copy + Into<CodePoint> + From<u8> {
191 fn to_char_lossy(self) -> char;
192
193 fn eq_char(self, c: char) -> bool;
194}
195
196impl FormatBuf for String {
197 type Char = char;
198
199 fn chars(&self) -> impl Iterator<Item = Self::Char> {
200 (**self).chars()
201 }
202
203 fn len(&self) -> usize {
204 self.len()
205 }
206
207 fn concat(mut self, other: Self) -> Self {
208 self.extend([other]);
209 self
210 }
211}
212
213impl FormatChar for char {
214 fn to_char_lossy(self) -> char {
215 self
216 }
217
218 fn eq_char(self, c: char) -> bool {
219 self == c
220 }
221}
222
223impl FormatBuf for Wtf8Buf {
224 type Char = CodePoint;
225
226 fn chars(&self) -> impl Iterator<Item = Self::Char> {
227 self.code_points()
228 }
229
230 fn len(&self) -> usize {
231 (**self).len()
232 }
233
234 fn concat(mut self, other: Self) -> Self {
235 self.extend([other]);
236 self
237 }
238}
239
240impl FormatChar for CodePoint {
241 fn to_char_lossy(self) -> char {
242 self.to_char_lossy()
243 }
244
245 fn eq_char(self, c: char) -> bool {
246 self == c
247 }
248}
249
250impl FormatBuf for Vec<u8> {
251 type Char = u8;
252
253 fn chars(&self) -> impl Iterator<Item = Self::Char> {
254 self.iter().copied()
255 }
256
257 fn len(&self) -> usize {
258 self.len()
259 }
260
261 fn concat(mut self, other: Self) -> Self {
262 self.extend(other);
263 self
264 }
265}
266
267impl FormatChar for u8 {
268 fn to_char_lossy(self) -> char {
269 self.into()
270 }
271
272 fn eq_char(self, c: char) -> bool {
273 char::from(self) == c
274 }
275}
276
277#[derive(Clone, Copy, Debug, Eq, PartialEq)]
278pub struct CFormatSpec {
279 pub flags: CConversionFlags,
280 pub min_field_width: Option<CFormatQuantity>,
281 pub precision: Option<CFormatPrecision>,
282 pub format_type: CFormatType,
283 }
285
286#[derive(Debug, Eq, PartialEq)]
287pub struct CFormatSpecKeyed<T> {
288 pub mapping_key: Option<T>,
289 pub spec: CFormatSpec,
290}
291
292#[cfg(test)]
293impl FromStr for CFormatSpec {
294 type Err = ParsingError;
295
296 fn from_str(text: &str) -> Result<Self, Self::Err> {
297 text.parse::<CFormatSpecKeyed<String>>()
298 .map(|CFormatSpecKeyed { mapping_key, spec }| {
299 assert!(mapping_key.is_none());
300 spec
301 })
302 }
303}
304
305impl FromStr for CFormatSpecKeyed<String> {
306 type Err = ParsingError;
307
308 fn from_str(text: &str) -> Result<Self, Self::Err> {
309 let mut chars = text.chars().enumerate().peekable();
310 if chars.next().map(|x| x.1) != Some('%') {
311 return Err((CFormatErrorType::MissingModuloSign, 1));
312 }
313
314 Self::parse(&mut chars, CFormatContext::Str)
315 }
316}
317
318pub type ParseIter<I> = Peekable<Enumerate<I>>;
319
320impl<T: FormatBuf> CFormatSpecKeyed<T> {
321 pub fn parse<I>(iter: &mut ParseIter<I>, context: CFormatContext) -> Result<Self, ParsingError>
322 where
323 I: Iterator<Item = T::Char>,
324 {
325 let mapping_key = parse_spec_mapping_key(iter)?;
326 let flags = parse_flags(iter);
327 let min_field_width =
328 parse_quantity(iter, isize::MAX as usize, CFormatErrorType::WidthTooBig)?;
329 let precision = parse_precision(iter)?;
330 consume_length(iter);
331 let format_type = parse_format_type(iter, context)?;
332
333 let spec = CFormatSpec {
334 flags,
335 min_field_width,
336 precision,
337 format_type,
338 };
339 Ok(Self { mapping_key, spec })
340 }
341}
342
343impl CFormatSpec {
344 fn compute_fill_string<T: FormatBuf>(fill_char: T::Char, fill_chars_needed: usize) -> T {
345 (0..fill_chars_needed).map(|_| fill_char).collect()
346 }
347
348 fn fill_string<T: FormatBuf>(
349 &self,
350 string: T,
351 fill_char: T::Char,
352 num_prefix_chars: Option<usize>,
353 ) -> T {
354 let mut num_chars = string.chars().count();
355 if let Some(num_prefix_chars) = num_prefix_chars {
356 num_chars += num_prefix_chars;
357 }
358 let num_chars = num_chars;
359
360 let width = match &self.min_field_width {
361 Some(CFormatQuantity::Amount(width)) => cmp::max(width, &num_chars),
362 _ => &num_chars,
363 };
364 let fill_chars_needed = width.saturating_sub(num_chars);
365 let fill_string: T = Self::compute_fill_string(fill_char, fill_chars_needed);
366
367 if !fill_string.is_empty() {
368 if self.flags.contains(CConversionFlags::LEFT_ADJUST) {
369 string.concat(fill_string)
370 } else {
371 fill_string.concat(string)
372 }
373 } else {
374 string
375 }
376 }
377
378 fn fill_string_with_precision<T: FormatBuf>(&self, string: T, fill_char: T::Char) -> T {
379 let num_chars = string.chars().count();
380
381 let width = match &self.precision {
382 Some(CFormatPrecision::Quantity(CFormatQuantity::Amount(width))) => {
383 cmp::max(width, &num_chars)
384 }
385 _ => &num_chars,
386 };
387 let fill_chars_needed = width.saturating_sub(num_chars);
388 let fill_string: T = Self::compute_fill_string(fill_char, fill_chars_needed);
389
390 if !fill_string.is_empty() {
391 fill_string.concat(string)
395 } else {
396 string
397 }
398 }
399
400 fn format_string_with_precision<T: FormatBuf>(
401 &self,
402 string: T,
403 precision: Option<&CFormatPrecision>,
404 ) -> T {
405 let string = match precision {
407 Some(CFormatPrecision::Quantity(CFormatQuantity::Amount(precision)))
408 if string.chars().count() > *precision =>
409 {
410 string.chars().take(*precision).collect::<T>()
411 }
412 Some(CFormatPrecision::Dot) => {
413 T::default()
415 }
416 _ => string,
417 };
418 self.fill_string(string, b' '.into(), None)
419 }
420
421 #[inline]
422 pub fn format_string<T: FormatBuf>(&self, string: T) -> T {
423 self.format_string_with_precision(string, self.precision.as_ref())
424 }
425
426 #[inline]
427 pub fn format_char<T: FormatBuf>(&self, ch: T::Char) -> T {
428 self.format_string_with_precision(
429 T::from_iter([ch]),
430 Some(&(CFormatQuantity::Amount(1).into())),
431 )
432 }
433
434 #[must_use]
435 pub fn format_bytes(&self, bytes: &[u8]) -> Vec<u8> {
436 let bytes = match self.precision {
437 Some(CFormatPrecision::Quantity(CFormatQuantity::Amount(precision))) => {
438 &bytes[..cmp::min(bytes.len(), precision)]
439 }
440 Some(CFormatPrecision::Dot) => &bytes[..0],
442 _ => bytes,
443 };
444 if let Some(CFormatQuantity::Amount(width)) = self.min_field_width {
445 let fill = width.saturating_sub(bytes.len());
446 let mut v = Vec::with_capacity(bytes.len() + fill);
447 if self.flags.contains(CConversionFlags::LEFT_ADJUST) {
448 v.extend_from_slice(bytes);
449 v.append(&mut vec![b' '; fill]);
450 } else {
451 v.append(&mut vec![b' '; fill]);
452 v.extend_from_slice(bytes);
453 }
454 v
455 } else {
456 bytes.to_vec()
457 }
458 }
459
460 #[must_use]
461 pub fn format_number(&self, num: &BigInt) -> String {
462 let CFormatType::Number(format_type) = self.format_type else {
463 unreachable!()
464 };
465
466 let magnitude = num.abs();
467 let prefix = if self.flags.contains(CConversionFlags::ALTERNATE_FORM) {
468 match format_type {
469 CNumberType::Octal => "0o",
470 CNumberType::HexLower => "0x",
471 CNumberType::HexUpper => "0X",
472 _ => "",
473 }
474 } else {
475 ""
476 };
477
478 let magnitude_string: String = match format_type {
479 CNumberType::DecimalD | CNumberType::DecimalI | CNumberType::DecimalU => {
480 magnitude.to_str_radix(10)
481 }
482 CNumberType::Octal => magnitude.to_str_radix(8),
483 CNumberType::HexLower => magnitude.to_str_radix(16),
484 CNumberType::HexUpper => {
485 let mut result = magnitude.to_str_radix(16);
486 result.make_ascii_uppercase();
487 result
488 }
489 };
490
491 let sign_string = match num.sign() {
492 Sign::Minus => "-",
493 _ => self.flags.sign_string(),
494 };
495
496 let padded_magnitude_string = self.fill_string_with_precision(magnitude_string, '0');
497
498 if self.flags.contains(CConversionFlags::ZERO_PAD) {
499 let fill_char = if !self.flags.contains(CConversionFlags::LEFT_ADJUST) {
500 '0'
501 } else {
502 ' ' };
504 let signed_prefix = format!("{sign_string}{prefix}");
505 format!(
506 "{}{}",
507 signed_prefix,
508 self.fill_string(
509 padded_magnitude_string,
510 fill_char,
511 Some(signed_prefix.chars().count()),
512 ),
513 )
514 } else {
515 self.fill_string(
516 format!("{sign_string}{prefix}{padded_magnitude_string}"),
517 ' ',
518 None,
519 )
520 }
521 }
522
523 #[must_use]
524 pub fn format_float(&self, num: f64) -> String {
525 let sign_string = if num.is_sign_negative() && !num.is_nan() {
526 "-"
527 } else {
528 self.flags.sign_string()
529 };
530
531 let precision = match &self.precision {
532 Some(CFormatPrecision::Quantity(quantity)) => match quantity {
533 CFormatQuantity::Amount(amount) => *amount,
534 CFormatQuantity::FromValuesTuple => 6,
535 },
536 Some(CFormatPrecision::Dot) => 0,
537 None => 6,
538 };
539
540 let CFormatType::Float(format_type) = self.format_type else {
541 unreachable!()
542 };
543
544 let magnitude = num.abs();
545 let case = format_type.case();
546
547 let magnitude_string = match format_type {
548 CFloatType::PointDecimalLower | CFloatType::PointDecimalUpper => float::format_fixed(
549 precision,
550 magnitude,
551 case,
552 self.flags.contains(CConversionFlags::ALTERNATE_FORM),
553 ),
554 CFloatType::ExponentLower | CFloatType::ExponentUpper => float::format_exponent(
555 precision,
556 magnitude,
557 case,
558 self.flags.contains(CConversionFlags::ALTERNATE_FORM),
559 ),
560 CFloatType::GeneralLower | CFloatType::GeneralUpper => {
561 let precision = if precision == 0 { 1 } else { precision };
562 float::format_general(
563 precision,
564 magnitude,
565 case,
566 self.flags.contains(CConversionFlags::ALTERNATE_FORM),
567 false,
568 )
569 }
570 };
571
572 if self.flags.contains(CConversionFlags::ZERO_PAD) {
573 let fill_char = if !self.flags.contains(CConversionFlags::LEFT_ADJUST) {
574 '0'
575 } else {
576 ' '
577 };
578 format!(
579 "{}{}",
580 sign_string,
581 self.fill_string(
582 magnitude_string,
583 fill_char,
584 Some(sign_string.chars().count()),
585 )
586 )
587 } else {
588 self.fill_string(format!("{sign_string}{magnitude_string}"), ' ', None)
589 }
590 }
591}
592
593fn parse_spec_mapping_key<T, I>(iter: &mut ParseIter<I>) -> Result<Option<T>, ParsingError>
594where
595 T: FormatBuf,
596 I: Iterator<Item = T::Char>,
597{
598 if let Some((index, _)) = iter.next_if(|(_, c)| c.eq_char('(')) {
599 return match parse_text_inside_parentheses(iter) {
600 Some(key) => Ok(Some(key)),
601 None => Err((CFormatErrorType::UnmatchedKeyParentheses, index)),
602 };
603 }
604 Ok(None)
605}
606
607fn parse_flags<C, I>(iter: &mut ParseIter<I>) -> CConversionFlags
608where
609 C: FormatChar,
610 I: Iterator<Item = C>,
611{
612 let mut flags = CConversionFlags::empty();
613 iter.peeking_take_while(|(_, c)| {
614 let flag = match c.to_char_lossy() {
615 '#' => CConversionFlags::ALTERNATE_FORM,
616 '0' => CConversionFlags::ZERO_PAD,
617 '-' => CConversionFlags::LEFT_ADJUST,
618 ' ' => CConversionFlags::BLANK_SIGN,
619 '+' => CConversionFlags::SIGN_CHAR,
620 _ => return false,
621 };
622 flags |= flag;
623 true
624 })
625 .for_each(drop);
626 flags
627}
628
629fn consume_length<C, I>(iter: &mut ParseIter<I>)
630where
631 C: FormatChar,
632 I: Iterator<Item = C>,
633{
634 iter.next_if(|(_, c)| matches!(c.to_char_lossy(), 'h' | 'l' | 'L'));
635}
636
637fn parse_format_type<C, I>(
638 iter: &mut ParseIter<I>,
639 context: CFormatContext,
640) -> Result<CFormatType, ParsingError>
641where
642 C: FormatChar,
643 I: Iterator<Item = C>,
644{
645 let (index, c) = iter.next().ok_or_else(|| {
646 (
647 CFormatErrorType::IncompleteFormat,
648 iter.peek().map_or(0, |x| x.0),
649 )
650 })?;
651
652 Ok(match c.to_char_lossy() {
653 'd' => CFormatType::Number(CNumberType::DecimalD),
654 'i' => CFormatType::Number(CNumberType::DecimalI),
655 'u' => CFormatType::Number(CNumberType::DecimalU),
656 'o' => CFormatType::Number(CNumberType::Octal),
657 'x' => CFormatType::Number(CNumberType::HexLower),
658 'X' => CFormatType::Number(CNumberType::HexUpper),
659 'e' => CFormatType::Float(CFloatType::ExponentLower),
660 'E' => CFormatType::Float(CFloatType::ExponentUpper),
661 'f' => CFormatType::Float(CFloatType::PointDecimalLower),
662 'F' => CFormatType::Float(CFloatType::PointDecimalUpper),
663 'g' => CFormatType::Float(CFloatType::GeneralLower),
664 'G' => CFormatType::Float(CFloatType::GeneralUpper),
665 'c' => CFormatType::Character(CCharacterType::Character),
666 'r' => CFormatType::String(CFormatConversion::Repr),
667 's' => CFormatType::String(CFormatConversion::Str),
668 'a' => CFormatType::String(CFormatConversion::Ascii),
669 'b' if context == CFormatContext::Bytes => CFormatType::Bytes,
670 _ => CFormatType::Unsupported {
671 ch: c.into(),
672 index,
673 },
674 })
675}
676
677fn parse_quantity<C, I>(
678 iter: &mut ParseIter<I>,
679 max_value: usize,
680 too_big: CFormatErrorType,
681) -> Result<Option<CFormatQuantity>, ParsingError>
682where
683 C: FormatChar,
684 I: Iterator<Item = C>,
685{
686 if let Some(&(_, c)) = iter.peek() {
687 if c.eq_char('*') {
688 iter.next().unwrap();
689 return Ok(Some(CFormatQuantity::FromValuesTuple));
690 }
691 if let Some(i) = c.to_char_lossy().to_digit(10) {
692 let mut num = i as usize;
693 iter.next().unwrap();
694 while let Some(&(index, c)) = iter.peek() {
695 if let Some(i) = c.to_char_lossy().to_digit(10) {
696 num = num
697 .checked_mul(10)
698 .and_then(|num| num.checked_add(i as usize))
699 .filter(|&num| num <= max_value)
700 .ok_or((too_big, index))?;
701 iter.next().unwrap();
702 } else {
703 break;
704 }
705 }
706 return Ok(Some(CFormatQuantity::Amount(num)));
707 }
708 }
709 Ok(None)
710}
711
712fn parse_precision<C, I>(iter: &mut ParseIter<I>) -> Result<Option<CFormatPrecision>, ParsingError>
713where
714 C: FormatChar,
715 I: Iterator<Item = C>,
716{
717 if iter.next_if(|(_, c)| c.eq_char('.')).is_some() {
718 let quantity = parse_quantity(iter, i32::MAX as usize, CFormatErrorType::PrecisionTooBig)?;
719 let precision = quantity.map_or(CFormatPrecision::Dot, CFormatPrecision::Quantity);
720 return Ok(Some(precision));
721 }
722 Ok(None)
723}
724
725fn parse_text_inside_parentheses<T, I>(iter: &mut ParseIter<I>) -> Option<T>
726where
727 T: FormatBuf,
728 I: Iterator<Item = T::Char>,
729{
730 let mut counter: i32 = 1;
731 let mut contained_text = T::default();
732 loop {
733 let (_, c) = iter.next()?;
734 match c.to_char_lossy() {
735 '(' => {
736 counter += 1;
737 }
738 ')' => {
739 counter -= 1;
740 }
741 _ => (),
742 }
743
744 if counter > 0 {
745 contained_text.extend([c]);
746 } else {
747 break;
748 }
749 }
750
751 Some(contained_text)
752}
753
754#[derive(Debug, Eq, PartialEq)]
755pub enum CFormatPart<T> {
756 Literal(T),
757 Spec(CFormatSpecKeyed<T>),
758}
759
760impl<T> CFormatPart<T> {
761 #[inline]
762 pub const fn is_specifier(&self) -> bool {
763 matches!(self, Self::Spec { .. })
764 }
765
766 #[inline]
767 pub const fn has_key(&self) -> bool {
768 match self {
769 Self::Spec(s) => s.mapping_key.is_some(),
770 _ => false,
771 }
772 }
773}
774
775#[derive(Debug, Eq, PartialEq)]
776pub struct CFormatStrOrBytes<S> {
777 parts: Vec<(usize, CFormatPart<S>)>,
778}
779
780impl<S> CFormatStrOrBytes<S> {
781 #[must_use]
782 pub fn check_specifiers(&self) -> Option<(usize, bool)> {
783 let mut count = 0;
784 let mut mapping_required = false;
785 for (_, part) in &self.parts {
786 if part.is_specifier() {
787 let has_key = part.has_key();
788 if count == 0 {
789 mapping_required = has_key;
790 } else if mapping_required != has_key {
791 return None;
792 }
793 count += 1;
794 }
795 }
796 Some((count, mapping_required))
797 }
798
799 #[inline]
800 pub fn iter(&self) -> impl Iterator<Item = &(usize, CFormatPart<S>)> {
801 self.parts.iter()
802 }
803
804 #[inline]
805 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (usize, CFormatPart<S>)> {
806 self.parts.iter_mut()
807 }
808
809 pub fn parse<I>(iter: &mut ParseIter<I>, context: CFormatContext) -> Result<Self, CFormatError>
810 where
811 S: FormatBuf,
812 I: Iterator<Item = S::Char>,
813 {
814 let mut parts = vec![];
815 let mut literal = S::default();
816 let mut part_index = 0;
817 while let Some((index, c)) = iter.next() {
818 if c.eq_char('%') {
819 if let Some(&(_, second)) = iter.peek() {
820 if second.eq_char('%') {
821 iter.next().unwrap();
822 literal.extend([second]);
823 continue;
824 }
825
826 if !literal.is_empty() {
827 parts.push((
828 part_index,
829 CFormatPart::Literal(core::mem::take(&mut literal)),
830 ));
831 }
832
833 let spec =
834 CFormatSpecKeyed::parse(iter, context).map_err(|err| CFormatError {
835 typ: err.0,
836 index: err.1,
837 })?;
838
839 parts.push((index, CFormatPart::Spec(spec)));
840 if let Some(&(index, _)) = iter.peek() {
841 part_index = index;
842 }
843 } else {
844 return Err(CFormatError {
845 typ: CFormatErrorType::IncompleteFormat,
846 index: index + 1,
847 });
848 }
849 } else {
850 literal.extend([c]);
851 }
852 }
853 if !literal.is_empty() {
854 parts.push((part_index, CFormatPart::Literal(literal)));
855 }
856 Ok(Self { parts })
857 }
858}
859
860impl<S> IntoIterator for CFormatStrOrBytes<S> {
861 type Item = (usize, CFormatPart<S>);
862 type IntoIter = alloc::vec::IntoIter<Self::Item>;
863
864 fn into_iter(self) -> Self::IntoIter {
865 self.parts.into_iter()
866 }
867}
868
869pub type CFormatBytes = CFormatStrOrBytes<Vec<u8>>;
870
871impl CFormatBytes {
872 pub fn parse_from_bytes(bytes: &[u8]) -> Result<Self, CFormatError> {
873 let mut iter = bytes.iter().copied().enumerate().peekable();
874 Self::parse(&mut iter, CFormatContext::Bytes)
875 }
876}
877
878pub type CFormatString = CFormatStrOrBytes<String>;
879
880impl FromStr for CFormatString {
881 type Err = CFormatError;
882
883 fn from_str(text: &str) -> Result<Self, Self::Err> {
884 let mut iter = text.chars().enumerate().peekable();
885 Self::parse(&mut iter, CFormatContext::Str)
886 }
887}
888
889pub type CFormatWtf8 = CFormatStrOrBytes<Wtf8Buf>;
890
891impl CFormatWtf8 {
892 pub fn parse_from_wtf8(s: &Wtf8) -> Result<Self, CFormatError> {
893 let mut iter = s.code_points().enumerate().peekable();
894 Self::parse(&mut iter, CFormatContext::Str)
895 }
896}
897
898#[cfg(test)]
899mod tests {
900 use super::*;
901
902 #[test]
903 fn fill_and_align() {
904 assert_eq!(
905 "%10s"
906 .parse::<CFormatSpec>()
907 .unwrap()
908 .format_string("test".to_owned()),
909 " test".to_owned()
910 );
911 assert_eq!(
912 "%-10s"
913 .parse::<CFormatSpec>()
914 .unwrap()
915 .format_string("test".to_owned()),
916 "test ".to_owned()
917 );
918 assert_eq!(
919 "%#10x"
920 .parse::<CFormatSpec>()
921 .unwrap()
922 .format_number(&BigInt::from(0x1337)),
923 " 0x1337".to_owned()
924 );
925 assert_eq!(
926 "%-#10x"
927 .parse::<CFormatSpec>()
928 .unwrap()
929 .format_number(&BigInt::from(0x1337)),
930 "0x1337 ".to_owned()
931 );
932 }
933
934 #[test]
935 fn parse_key() {
936 let expected = Ok(CFormatSpecKeyed {
937 mapping_key: Some("amount".to_owned()),
938 spec: CFormatSpec {
939 format_type: CFormatType::Number(CNumberType::DecimalD),
940 min_field_width: None,
941 precision: None,
942 flags: CConversionFlags::empty(),
943 },
944 });
945 assert_eq!("%(amount)d".parse::<CFormatSpecKeyed<String>>(), expected);
946
947 let expected = Ok(CFormatSpecKeyed {
948 mapping_key: Some("m((u(((l((((ti))))p)))l))e".to_owned()),
949 spec: CFormatSpec {
950 format_type: CFormatType::Number(CNumberType::DecimalD),
951 min_field_width: None,
952 precision: None,
953 flags: CConversionFlags::empty(),
954 },
955 });
956 assert_eq!(
957 "%(m((u(((l((((ti))))p)))l))e)d".parse::<CFormatSpecKeyed<String>>(),
958 expected
959 );
960 }
961
962 #[test]
963 fn format_parse_key_fail() {
964 assert_eq!(
965 "%(aged".parse::<CFormatString>(),
966 Err(CFormatError {
967 typ: CFormatErrorType::UnmatchedKeyParentheses,
968 index: 1
969 })
970 );
971 }
972
973 #[test]
974 fn format_parse_type_fail() {
975 let parsed = "Hello %n".parse::<CFormatString>().unwrap();
976 let spec = parsed.iter().find_map(|(_, part)| match part {
977 CFormatPart::Spec(spec) => Some(&spec.spec),
978 CFormatPart::Literal(_) => None,
979 });
980 assert!(
981 matches!(
982 spec.map(|s| &s.format_type),
983 Some(CFormatType::Unsupported { ch, index: 7 }) if *ch == 'n'
984 ),
985 "{spec:?}"
986 );
987 }
988
989 #[test]
990 fn incomplete_format_fail() {
991 assert_eq!(
992 "Hello %".parse::<CFormatString>(),
993 Err(CFormatError {
994 typ: CFormatErrorType::IncompleteFormat,
995 index: 7
996 })
997 );
998 }
999
1000 #[test]
1001 fn width_and_precision_have_distinct_limits_and_errors() {
1002 let precision = "%.2147483648f".parse::<CFormatSpec>().unwrap_err();
1003 assert_eq!(precision.0, CFormatErrorType::PrecisionTooBig);
1004 assert_eq!(
1005 CFormatError {
1006 typ: precision.0,
1007 index: precision.1,
1008 }
1009 .to_string(),
1010 "precision too big"
1011 );
1012
1013 let oversized_width = format!("%{}f", isize::MAX as u128 + 1);
1014 let width = oversized_width.parse::<CFormatSpec>().unwrap_err();
1015 assert_eq!(width.0, CFormatErrorType::WidthTooBig);
1016 assert_eq!(
1017 CFormatError {
1018 typ: width.0,
1019 index: width.1,
1020 }
1021 .to_string(),
1022 "width too big"
1023 );
1024
1025 if usize::BITS > 32 {
1026 let spec = "%2147483648f".parse::<CFormatSpec>().unwrap();
1027 assert_eq!(
1028 spec.min_field_width,
1029 Some(CFormatQuantity::Amount(2_147_483_648))
1030 );
1031 }
1032 }
1033
1034 #[test]
1035 fn parse_flags() {
1036 let expected = Ok(CFormatSpec {
1037 format_type: CFormatType::Number(CNumberType::DecimalD),
1038 min_field_width: Some(CFormatQuantity::Amount(10)),
1039 precision: None,
1040 flags: CConversionFlags::all(),
1041 });
1042 let parsed = "% 0 -+++###10d".parse::<CFormatSpec>();
1043 assert_eq!(parsed, expected);
1044 assert_eq!(
1045 parsed.unwrap().format_number(&BigInt::from(12)),
1046 "+12 ".to_owned()
1047 );
1048 }
1049
1050 #[test]
1051 fn parse_and_format_string() {
1052 assert_eq!(
1053 "%5.4s"
1054 .parse::<CFormatSpec>()
1055 .unwrap()
1056 .format_string("Hello, World!".to_owned()),
1057 " Hell".to_owned()
1058 );
1059 assert_eq!(
1060 "%-5.4s"
1061 .parse::<CFormatSpec>()
1062 .unwrap()
1063 .format_string("Hello, World!".to_owned()),
1064 "Hell ".to_owned()
1065 );
1066 assert_eq!(
1067 "%.s"
1068 .parse::<CFormatSpec>()
1069 .unwrap()
1070 .format_string("Hello, World!".to_owned()),
1071 "".to_owned()
1072 );
1073 assert_eq!(
1074 "%5.s"
1075 .parse::<CFormatSpec>()
1076 .unwrap()
1077 .format_string("Hello, World!".to_owned()),
1078 " ".to_owned()
1079 );
1080 }
1081
1082 #[test]
1083 fn parse_and_format_unicode_string() {
1084 assert_eq!(
1085 "%.2s"
1086 .parse::<CFormatSpec>()
1087 .unwrap()
1088 .format_string("❤❤❤❤❤❤❤❤".to_owned()),
1089 "❤❤".to_owned()
1090 );
1091 }
1092
1093 #[test]
1094 fn parse_and_format_number() {
1095 assert_eq!(
1096 "%5d"
1097 .parse::<CFormatSpec>()
1098 .unwrap()
1099 .format_number(&BigInt::from(27)),
1100 " 27".to_owned()
1101 );
1102 assert_eq!(
1103 "%05d"
1104 .parse::<CFormatSpec>()
1105 .unwrap()
1106 .format_number(&BigInt::from(27)),
1107 "00027".to_owned()
1108 );
1109 assert_eq!(
1110 "%.5d"
1111 .parse::<CFormatSpec>()
1112 .unwrap()
1113 .format_number(&BigInt::from(27)),
1114 "00027".to_owned()
1115 );
1116 assert_eq!(
1117 "%+05d"
1118 .parse::<CFormatSpec>()
1119 .unwrap()
1120 .format_number(&BigInt::from(27)),
1121 "+0027".to_owned()
1122 );
1123 assert_eq!(
1124 "%-d"
1125 .parse::<CFormatSpec>()
1126 .unwrap()
1127 .format_number(&BigInt::from(-27)),
1128 "-27".to_owned()
1129 );
1130 assert_eq!(
1131 "% d"
1132 .parse::<CFormatSpec>()
1133 .unwrap()
1134 .format_number(&BigInt::from(27)),
1135 " 27".to_owned()
1136 );
1137 assert_eq!(
1138 "% d"
1139 .parse::<CFormatSpec>()
1140 .unwrap()
1141 .format_number(&BigInt::from(-27)),
1142 "-27".to_owned()
1143 );
1144 assert_eq!(
1145 "%08x"
1146 .parse::<CFormatSpec>()
1147 .unwrap()
1148 .format_number(&BigInt::from(0x1337)),
1149 "00001337".to_owned()
1150 );
1151 assert_eq!(
1152 "%#010x"
1153 .parse::<CFormatSpec>()
1154 .unwrap()
1155 .format_number(&BigInt::from(0x1337)),
1156 "0x00001337".to_owned()
1157 );
1158 assert_eq!(
1159 "%-#010x"
1160 .parse::<CFormatSpec>()
1161 .unwrap()
1162 .format_number(&BigInt::from(0x1337)),
1163 "0x1337 ".to_owned()
1164 );
1165 }
1166
1167 #[test]
1168 fn parse_and_format_float() {
1169 assert_eq!(
1170 "%f".parse::<CFormatSpec>().unwrap().format_float(1.2345),
1171 "1.234500"
1172 );
1173 assert_eq!(
1174 "%.2f".parse::<CFormatSpec>().unwrap().format_float(1.2345),
1175 "1.23"
1176 );
1177 assert_eq!(
1178 "%.f".parse::<CFormatSpec>().unwrap().format_float(1.2345),
1179 "1"
1180 );
1181 assert_eq!(
1182 "%+.f".parse::<CFormatSpec>().unwrap().format_float(1.2345),
1183 "+1"
1184 );
1185 assert_eq!(
1186 "%+f".parse::<CFormatSpec>().unwrap().format_float(1.2345),
1187 "+1.234500"
1188 );
1189 assert_eq!(
1190 "% f".parse::<CFormatSpec>().unwrap().format_float(1.2345),
1191 " 1.234500"
1192 );
1193 assert_eq!(
1194 "%f".parse::<CFormatSpec>().unwrap().format_float(-1.2345),
1195 "-1.234500"
1196 );
1197 assert_eq!(
1198 "%f".parse::<CFormatSpec>()
1199 .unwrap()
1200 .format_float(1.2345678901),
1201 "1.234568"
1202 );
1203 }
1204
1205 #[test]
1206 fn format_parse() {
1207 let fmt = "Hello, my name is %s and I'm %d years old";
1208 let expected = Ok(CFormatString {
1209 parts: vec![
1210 (0, CFormatPart::Literal("Hello, my name is ".to_owned())),
1211 (
1212 18,
1213 CFormatPart::Spec(CFormatSpecKeyed {
1214 mapping_key: None,
1215 spec: CFormatSpec {
1216 format_type: CFormatType::String(CFormatConversion::Str),
1217 min_field_width: None,
1218 precision: None,
1219 flags: CConversionFlags::empty(),
1220 },
1221 }),
1222 ),
1223 (20, CFormatPart::Literal(" and I'm ".to_owned())),
1224 (
1225 29,
1226 CFormatPart::Spec(CFormatSpecKeyed {
1227 mapping_key: None,
1228 spec: CFormatSpec {
1229 format_type: CFormatType::Number(CNumberType::DecimalD),
1230 min_field_width: None,
1231 precision: None,
1232 flags: CConversionFlags::empty(),
1233 },
1234 }),
1235 ),
1236 (31, CFormatPart::Literal(" years old".to_owned())),
1237 ],
1238 });
1239 let result = fmt.parse::<CFormatString>();
1240 assert_eq!(
1241 result, expected,
1242 "left = {result:#?} \n\n\n right = {expected:#?}"
1243 );
1244 }
1245}