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